Import GtkRecent* from libegg.
authorEmmanuele Bassi <ebassi@cvs.gnome.org>
Wed, 29 Mar 2006 20:16:44 +0000 (20:16 +0000)
committerEmmanuele Bassi <ebassi@src.gnome.org>
Wed, 29 Mar 2006 20:16:44 +0000 (20:16 +0000)
2006-03-29  Emmanuele Bassi  <ebassi@cvs.gnome.org>

Import GtkRecent* from libegg.

* gtk/gtkrecentmanager.[ch]: Add GtkRecentManager, an object
for managing a list of recently used resources.

* gtk/gtkrecentchooser.[ch]:
* gtk/gtkrecentchooserdefault.[ch]:
* gtk/gtkrecentchooserdialog.[ch]:
* gtk/gtkrecentchoosermenu.[ch]:
* gtk/gtkrecentchooserprivate.h:
* gtk/gtkrecentchooserutils.[ch]:
* gtk/gtkrecentchooserwidget.[ch]: Add GtkRecentChooser, a
GTypeInterface for widgets implementing viewers for recently
used resources.

* gtk/gtkrecentfilter.[ch]: GtkRecentFilter, a filter object
for GtkRecentChooser implementations.

* gtk/gtk.h:
* gtk/gtk.symbols:
* gtk/Makefile.am: Build glue for GtkRecent*.

20 files changed:
gtk/Makefile.am
gtk/gtk.h
gtk/gtk.symbols
gtk/gtkrecentchooser.c [new file with mode: 0644]
gtk/gtkrecentchooser.h [new file with mode: 0644]
gtk/gtkrecentchooserdefault.c [new file with mode: 0644]
gtk/gtkrecentchooserdefault.h [new file with mode: 0644]
gtk/gtkrecentchooserdialog.c [new file with mode: 0644]
gtk/gtkrecentchooserdialog.h [new file with mode: 0644]
gtk/gtkrecentchoosermenu.c [new file with mode: 0644]
gtk/gtkrecentchoosermenu.h [new file with mode: 0644]
gtk/gtkrecentchooserprivate.h [new file with mode: 0644]
gtk/gtkrecentchooserutils.c [new file with mode: 0644]
gtk/gtkrecentchooserutils.h [new file with mode: 0644]
gtk/gtkrecentchooserwidget.c [new file with mode: 0644]
gtk/gtkrecentchooserwidget.h [new file with mode: 0644]
gtk/gtkrecentfilter.c [new file with mode: 0644]
gtk/gtkrecentfilter.h [new file with mode: 0644]
gtk/gtkrecentmanager.c [new file with mode: 0644]
gtk/gtkrecentmanager.h [new file with mode: 0644]

index 4d24311b877ecc2dacba14be5df5700442f23f78..1a9f19a1978febae889e2e834f443d25dd0b6899 100644 (file)
@@ -228,6 +228,12 @@ gtk_public_h_sources =          \
        gtkradiotoolbutton.h    \
        gtkrange.h              \
        gtkrc.h                 \
+       gtkrecentchooser.h      \
+       gtkrecentchooserdialog.h \
+       gtkrecentchoosermenu.h  \
+       gtkrecentchooserwidget.h \
+       gtkrecentfilter.h       \
+       gtkrecentmanager.h      \
        gtkruler.h              \
        gtkscale.h              \
        gtkscrollbar.h          \
@@ -310,6 +316,9 @@ gtk_private_h_sources =             \
        gtkpathbar.h            \
        gtkplugprivate.h        \
        gtkrbtree.h             \
+       gtkrecentchooserdefault.h \
+       gtkrecentchooserprivate.h \
+       gtkrecentchooserutils.h \
        gtksequence.h           \
        gtksocketprivate.h      \
        gtktextbtree.h          \
@@ -459,6 +468,14 @@ gtk_c_sources =                 \
        gtkrange.c              \
        gtkrbtree.c             \
        gtkrc.c                 \
+       gtkrecentchooserdefault.c \
+       gtkrecentchooserdialog.c \
+       gtkrecentchoosermenu.c  \
+       gtkrecentchooserwidget.c \
+       gtkrecentchooserutils.c \
+       gtkrecentchooser.c      \
+       gtkrecentfilter.c       \
+       gtkrecentmanager.c      \
        gtkruler.c              \
        gtkscale.c              \
        gtkscrollbar.c          \
index e99aa165c75103a5e8ead94edce986c792eb4777..2575238e7a94ae927d5cd0bfdb13a30271bdd99a 100644 (file)
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
 #include <gtk/gtkradiotoolbutton.h>
 #include <gtk/gtkrange.h>
 #include <gtk/gtkrc.h>
+#include <gtk/gtkrecentchooser.h>
+#include <gtk/gtkrecentchooserdialog.h>
+#include <gtk/gtkrecentchoosermenu.h>
+#include <gtk/gtkrecentchooserwidget.h>
+#include <gtk/gtkrecentfilter.h>
+#include <gtk/gtkrecentmanager.h>
 #include <gtk/gtkruler.h>
 #include <gtk/gtkscale.h>
 #include <gtk/gtkscrollbar.h>
index 24231aa871489e01b9ba4ee2ba75a9689d35c9ae..0403fa2b470bb9c7fa854244423ced8796d0bf6c 100644 (file)
@@ -2621,6 +2621,134 @@ gtk_rc_style_unref
 #endif
 #endif
 
+#if IN_HEADER(__GTK_RECENT_CHOOSER_H__)
+#if IN_FILE(__GTK_RECENT_CHOOSER_C__))
+gtk_recent_chooser_set_show_private
+gtk_recent_chooser_get_show_private
+gtk_recent_chooser_set_show_not_found
+gtk_recent_chooser_get_show_not_found
+gtk_recent_chooser_set_show_icons
+gtk_recent_chooser_get_show_icons
+gtk_recent_chooser_set_select_multiple
+gtk_recent_chooser_get_select_multiple
+gtk_recent_chooser_set_local_only
+gtk_recent_chooser_get_local_only
+gtk_recent_chooser_set_limit
+gtk_recent_chooser_get_limit
+gtk_recent_chooser_set_show_tips
+gtk_recent_chooser_get_show_tips
+gtk_recent_chooser_set_show_numbers
+gtk_recent_chooser_get_show_numbers
+gtk_recent_chooser_set_sort_type
+gtk_recent_chooser_get_sort_type
+gtk_recent_chooser_set_sort_func
+gtk_recent_chooser_set_current_uri
+gtk_recent_chooser_get_current_uri
+gtk_recent_chooser_get_current_item
+gtk_recent_chooser_select_uri
+gtk_recent_chooser_unselect_uri
+gtk_recent_chooser_select_all
+gtk_recent_chooser_unselect_all
+gtk_recent_chooser_get_items
+gtk_recent_chooser_get_uris
+gtk_recent_chooser_add_filter
+gtk_recent_chooser_remove_filter
+gtk_recent_chooser_list_filters
+gtk_recent_chooser_set_filter
+gtk_recent_chooser_get_filter
+gtk_recent_chooser_get_type G_GNUC_CONST
+gtk_recent_chooser_error_quark
+#endif
+#endif
+
+#if IN_HEADER(__GTK_RECENT_CHOOSER_DIALOG_H__)
+#if IN_FILE(__GTK_RECENT_CHOOSER_DIALOG_C__)
+gtk_recent_chooser_dialog_get_type G_GNUC_CONST
+gtk_recent_chooser_dialog_new G_GNUC_NULL_TERMINATED
+gtk_recent_chooser_dialog_new_for_manager G_GNUC_NULL_TERMINATED
+#endif
+#endif
+
+#if IN_HEADER(__GTK_RECENT_CHOOSER_MENU_H__)
+#if IN_FILE(__GTK_RECENT_CHOOSER_MENU_C__)
+gtk_recent_chooser_menu_get_type G_GNUC_CONST
+gtk_recent_chooser_menu_new
+gtk_recent_chooser_menu_new_for_manager
+gtk_recent_chooser_menu_get_show_numbers
+gtk_recent_chooser_menu_set_show_numbers
+#endif
+#endif
+
+#if IN_HEADER(__GTK_RECENT_CHOOSER_WIDGET_H__)
+#if IN_FILE(__GTK_RECENT_CHOOSER_WIDGET_C__)
+gtk_recent_chooser_widget_get_type G_GNUC_CONST
+gtk_recent_chooser_widget_new
+gtk_recent_chooser_widget_new_for_manager
+#endif
+#endif
+
+#if IN_HEADER(__GTK_RECENT_FILTER_H__)
+#if IN_FILE(__GTK_RECENT_FILTER_C__)
+gtk_recent_filter_get_type G_GNUC_CONST
+gtk_recent_filter_new
+gtk_recent_filter_set_name
+gtk_recent_filter_get_name
+gtk_recent_filter_add_mime_type
+gtk_recent_filter_add_pattern
+gtk_recent_filter_add_pixbuf_formats
+gtk_recent_filter_add_application
+gtk_recent_filter_add_group
+gtk_recent_filter_add_age
+gtk_recent_filter_add_custom
+gtk_recent_filter_get_needed
+gtk_recent_filter_filter
+#endif
+#endif
+
+#if IN_HEADER(__GTK_RECENT_MANAGER_H__)
+#if IN_FILE(__GTK_RECENT_MANAGER_C__)
+gtk_recent_manager_error_quark
+gtk_recent_manager_get_type G_GNUC_CONST
+gtk_recent_manager_new
+gtk_recent_manager_get_default
+gtk_recent_manager_get_for_screen
+gtk_recent_manager_set_screen
+gtk_recent_manager_add_item
+gtk_recent_manager_add_full
+gtk_recent_manager_remove_item
+gtk_recent_manager_lookup_item
+gtk_recent_manager_has_item
+gtk_recent_manager_move_item
+gtk_recent_manager_set_limit
+gtk_recent_manager_get_limit
+gtk_recent_manager_purge_items
+gtk_recent_info_get_type G_GNUC_CONST
+gtk_recent_info_ref
+gtk_recent_info_unref
+gtk_recent_info_get_uri
+gtk_recent_info_get_display_name
+gtk_recent_info_get_description
+gtk_recent_info_get_mime_type
+gtk_recent_info_get_added
+gtk_recent_info_get_modified
+gtk_recent_info_get_visited
+gtk_recent_info_get_private_hint
+gtk_recent_info_get_application_info
+gtk_recent_info_get_applications G_GNUC_MALLOC
+gtk_recent_info_last_application G_GNUC_MALLOC
+gtk_recent_info_has_application
+gtk_recent_info_get_groups G_GNUC_MALLOC
+gtk_recent_info_has_group
+gtk_recent_info_get_icon
+gtk_recent_info_get_short_name G_GNUC_MALLOC
+gtk_recent_info_get_uri_display G_GNUC_MALLOC
+gtk_recent_info_get_age
+gtk_recent_info_is_local
+gtk_recent_info_exists
+gtk_recent_info_match
+#endif
+#endif
+
 #if IN_HEADER(__GTK_TEXT_BUFFER_RICH_TEXT_H__)
 #if IN_FILE(__GTK_TEXT_BUFFER_RICH_TEXT_C__)
 gtk_text_buffer_deserialize
diff --git a/gtk/gtkrecentchooser.c b/gtk/gtkrecentchooser.c
new file mode 100644 (file)
index 0000000..873eee1
--- /dev/null
@@ -0,0 +1,966 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentchooser.c - Abstract interface for recent file selectors GUIs
+ *
+ * Copyright (C) 2006, Emmanuele Bassi
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gtkrecentchooser.h"
+#include "gtkrecentchooserprivate.h"
+#include "gtkrecentmanager.h"
+#include "gtkintl.h"
+#include "gtktypebuiltins.h"
+#include "gtkprivate.h"
+#include "gtkmarshalers.h"
+#include "gtkalias.h"
+
+
+enum
+{
+  ITEM_ACTIVATED,
+  SELECTION_CHANGED,
+
+  LAST_SIGNAL
+};
+
+static void gtk_recent_chooser_class_init (gpointer g_iface);
+
+static guint chooser_signals[LAST_SIGNAL] = { 0, };
+
+GType
+gtk_recent_chooser_get_type (void)
+{
+  static GType chooser_type = 0;
+  
+  if (!chooser_type)
+    {
+      static const GTypeInfo chooser_info =
+      {
+        sizeof (GtkRecentChooserIface),
+        NULL, /* base_init */
+        NULL, /* base_finalize */
+        (GClassInitFunc) gtk_recent_chooser_class_init,
+      };
+      
+      chooser_type = g_type_register_static (G_TYPE_INTERFACE,
+                                            I_("GtkRecentChooser"),
+                                            &chooser_info, 0);
+                                            
+      g_type_interface_add_prerequisite (chooser_type, GTK_TYPE_OBJECT);
+    }
+  
+  return chooser_type;
+}
+
+static void
+gtk_recent_chooser_class_init (gpointer g_iface)
+{
+  GType iface_type = G_TYPE_FROM_INTERFACE (g_iface);
+  
+  /**
+   * GtkRecentChooser::selection-changed
+   * @chooser: the object which received the signal
+   *
+   * This signal is emitted when there is a change in the set of
+   * selected recently used resources.  This can happen when a user
+   * modifies the selection with the mouse or the keyboard, or when
+   * explicitely calling functions to change the selection.
+   *
+   * Since: 2.10
+   */
+  chooser_signals[SELECTION_CHANGED] =
+    g_signal_new (I_("selection-changed"),
+                  iface_type,
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GtkRecentChooserIface, selection_changed),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+   
+  /**
+   * GtkRecentChooser::item-activated
+   * @chooser: the object which received the signal
+   *
+   * This signal is emitted when the user "activates" a recent item
+   * in the recent chooser.  This can happen by double-clicking on an item
+   * in the recently used resources list, or by pressing
+   * <keycap>Enter</keycap>.
+   *
+   * Since: 2.10
+   */
+  chooser_signals[ITEM_ACTIVATED] =
+    g_signal_new (I_("item-activated"),
+                  iface_type,
+                 G_SIGNAL_RUN_LAST,
+                 G_STRUCT_OFFSET (GtkRecentChooserIface, item_activated),
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__VOID,
+                 G_TYPE_NONE, 0);
+  g_object_interface_install_property (g_iface,
+                                      g_param_spec_object ("recent-manager",
+                                                           P_("Recent Manager"),
+                                                           P_("The RecentManager object to use"),
+                                                           GTK_TYPE_RECENT_MANAGER,
+                                                           G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_interface_install_property (g_iface,
+                                      g_param_spec_boolean ("show-private",
+                                                            P_("Show Private"),
+                                                            P_("Whether the private items should be displayed"),
+                                                            FALSE,
+                                                            G_PARAM_READWRITE));
+  g_object_interface_install_property (g_iface,
+                                      g_param_spec_boolean ("show-tips",
+                                                            P_("Show Tooltips"),
+                                                            P_("Whether there should be a tooltip on the item"),
+                                                            FALSE,
+                                                            G_PARAM_READWRITE));
+  g_object_interface_install_property (g_iface,
+                                      g_param_spec_boolean ("show-icons",
+                                                            P_("Show Icons"),
+                                                            P_("Whether there should be an icon near the item"),
+                                                            TRUE,
+                                                            G_PARAM_READWRITE));
+  g_object_interface_install_property (g_iface,
+                                      g_param_spec_boolean ("show-not-found",
+                                                            P_("Show Not Found"),
+                                                            P_("Whether the items pointing to unavailable resources should be displayed"),
+                                                            FALSE,
+                                                            G_PARAM_READWRITE));
+  g_object_interface_install_property (g_iface,
+                                      g_param_spec_boolean ("select-multiple",
+                                                            P_("Select Multiple"),
+                                                            P_("Whether to allow multiple items to be selected"),
+                                                            FALSE,
+                                                            G_PARAM_READWRITE));
+  g_object_interface_install_property (g_iface,
+                                      g_param_spec_boolean ("local-only",
+                                                            P_("Local only"),
+                                                            P_("Whether the selected resource(s) should be limited to local file: URIs"),
+                                                            TRUE,
+                                                            G_PARAM_READWRITE));
+  g_object_interface_install_property (g_iface,
+                                      g_param_spec_int ("limit",
+                                                        P_("Limit"),
+                                                        P_("The maximum number of items to be displayed"),
+                                                        -1,
+                                                        G_MAXINT,
+                                                        -1,
+                                                        G_PARAM_READWRITE));
+  g_object_interface_install_property (g_iface,
+                                      g_param_spec_enum ("sort-type",
+                                                         P_("Sort Type"),
+                                                         P_("The sorting order of the items displayed"),
+                                                         GTK_TYPE_RECENT_SORT_TYPE,
+                                                         GTK_RECENT_SORT_NONE,
+                                                         G_PARAM_READWRITE));
+  g_object_interface_install_property (g_iface,
+                                      g_param_spec_object ("filter",
+                                                           P_("Filter"),
+                                                           P_("The current filter for selecting which resources are displayed"),
+                                                           GTK_TYPE_RECENT_FILTER,
+                                                           G_PARAM_READWRITE));
+}
+
+GQuark
+gtk_recent_chooser_error_quark (void)
+{
+  static GQuark error_quark = 0;
+  if (!error_quark)
+    error_quark = g_quark_from_static_string ("gtk-recent-chooser-error-quark");
+  return error_quark;
+}
+
+/**
+ * _gtk_recent_chooser_get_recent_manager:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Gets the #GtkRecentManager used by @chooser.
+ *
+ * Return value: the recent manager for @chooser.
+ *
+ * Since: 2.10
+ */
+GtkRecentManager *
+_gtk_recent_chooser_get_recent_manager (GtkRecentChooser *chooser)
+{
+  g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), NULL);
+  
+  return GTK_RECENT_CHOOSER_GET_IFACE (chooser)->get_recent_manager (chooser);
+}
+
+/**
+ * gtk_recent_chooser_set_show_private:
+ * @chooser: a #GtkRecentChooser
+ * @show_private: %TRUE to show private items, %FALSE otherwise
+ *
+ * Whether to show recently used resources marked registered as private.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_set_show_private (GtkRecentChooser *chooser,
+                                    gboolean          show_private)
+{
+  g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+  
+  g_object_set (chooser, "show-private", show_private, NULL);
+}
+
+/**
+ * gtk_recent_chooser_get_show_private:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Returns whether @chooser should display recently used resources
+ * registered as private.
+ *
+ * Return value: %TRUE if the recent chooser should show private items,
+ *   %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_chooser_get_show_private (GtkRecentChooser *chooser)
+{
+  gboolean show_private;
+  
+  g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), FALSE);
+  
+  g_object_get (chooser, "show-private", &show_private, NULL);
+  
+  return show_private;
+}
+
+/**
+ * gtk_recent_chooser_set_show_not_found:
+ * @chooser: a #GtkRecentChooser
+ * @show_not_found: whether to show the local items we didn't find
+ *
+ * Sets whether @chooser should display the recently used resources that
+ * it didn't find.  This only applies to local resources.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_set_show_not_found (GtkRecentChooser *chooser,
+                                      gboolean          show_not_found)
+{
+  g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+  
+  g_object_set (chooser, "show-not-found", show_not_found, NULL);
+}
+
+/**
+ * gtk_recent_chooser_get_show_not_found:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Retrieves whether @chooser should show the recently used resources that
+ * were not found.
+ *
+ * Return value: %TRUE if the resources not found should be displayed, and
+ *   %FALSE otheriwse.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_chooser_get_show_not_found (GtkRecentChooser *chooser)
+{
+  gboolean show_not_found;
+  
+  g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), FALSE);
+  
+  g_object_get (chooser, "show-not-found", &show_not_found, NULL);
+  
+  return show_not_found;
+}
+
+/**
+ * gtk_recent_chooser_set_show_icons:
+ * @chooser: a #GtkRecentChooser
+ * @show_icons: whether to show an icon near the resource
+ *
+ * Sets whether @chooser should show an icon near the resource when
+ * displaying it.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_set_show_icons (GtkRecentChooser *chooser,
+                                  gboolean          show_icons)
+{
+  g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+  
+  g_object_set (chooser, "show-icons", show_icons, NULL);
+}
+
+/**
+ * gtk_recent_chooser_get_show_icons:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Retrieves whether @chooser should show an icon near the resource.
+ *
+ * Return value: %TRUE if the icons should be displayed, %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_chooser_get_show_icons (GtkRecentChooser *chooser)
+{
+  gboolean show_icons;
+  
+  g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), FALSE);
+  
+  g_object_get (chooser, "show-icons", &show_icons, NULL);
+  
+  return show_icons;
+}
+
+/**
+ * gtk_recent_chooser_set_select_multiple:
+ * @chooser: a #GtkRecentChooser
+ * @select_multiple: %TRUE if @chooser can select more than one item
+ *
+ * Sets whether @chooser can select multiple items.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_set_select_multiple (GtkRecentChooser *chooser,
+                                       gboolean          select_multiple)
+{
+  g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+  
+  g_object_set (chooser, "select-multiple", select_multiple, NULL);
+}
+
+/**
+ * gtk_recent_chooser_get_select_multiple:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Gets whether @chooser can select multiple items.
+ *
+ * Return value: %TRUE if @chooser can select more than one item.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_chooser_get_select_multiple (GtkRecentChooser *chooser)
+{
+  gboolean select_multiple;
+  
+  g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), FALSE);
+  
+  g_object_get (chooser, "select-multiple", &select_multiple, NULL);
+  
+  return select_multiple;
+}
+
+/**
+ * gtk_recent_chooser_set_local_only:
+ * @chooser: a #GtkRecentChooser
+ * @local_only: %TRUE if only local files can be shown
+ * 
+ * Sets whether only local resources, that is resources using the file:// URI
+ * scheme, should be shown in the recently used resources selector.  If
+ * @local_only is %TRUE (the default) then the shown resources are guaranteed
+ * to be accessible through the operating system native file system.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_set_local_only (GtkRecentChooser *chooser,
+                                  gboolean          local_only)
+{
+  g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+
+  g_object_set (chooser, "local-only", local_only, NULL);
+}
+
+/**
+ * gtk_recent_chooser_get_local_only:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Gets whether only local resources should be shown in the recently used
+ * resources selector.  See gtk_recent_chooser_set_local_only()
+ *
+ * Return value: %TRUE if only local resources should be shown.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_chooser_get_local_only (GtkRecentChooser *chooser)
+{
+  gboolean local_only;
+
+  g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), FALSE);
+
+  g_object_get (chooser, "local-only", &local_only, NULL);
+
+  return local_only;
+}
+
+/**
+ * gtk_recent_chooser_set_limit:
+ * @chooser: a #GtkRecentChooser
+ * @limit: a positive integer, or -1 for all items
+ *
+ * Sets the number of items that should be returned by
+ * gtk_recent_chooser_get_items() and gtk_recent_chooser_get_uris().
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_set_limit (GtkRecentChooser *chooser,
+                             gint              limit)
+{
+  g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+  
+  g_object_set (chooser, "limit", limit, NULL);
+}
+
+/**
+ * gtk_recent_chooser_get_limit:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Gets the number of items returned by gtk_recent_chooser_get_items()
+ * and gtk_recent_chooser_get_uris().
+ *
+ * Return value: A positive integer, or -1 meaning that all items are
+ *   returned.
+ *
+ * Since: 2.10
+ */
+gint
+gtk_recent_chooser_get_limit (GtkRecentChooser *chooser)
+{
+  gint limit;
+  
+  g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), 10);
+  
+  g_object_get (chooser, "limit", &limit, NULL);
+  
+  return limit;
+}
+
+/**
+ * gtk_recent_chooser_set_show_tips:
+ * @chooser: a #GtkRecentChooser
+ * @show_tips: %TRUE if tooltips should be shown
+ *
+ * Sets whether to show a tooltips on the widget.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_set_show_tips (GtkRecentChooser *chooser,
+                                 gboolean          show_tips)
+{
+  g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+  
+  g_object_set (chooser, "show-tips", show_tips, NULL);
+}
+
+/**
+ * gtk_recent_chooser_get_show_tips:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Gets whether @chooser should display tooltips.
+ *
+ * Return value: %TRUE if the recent chooser should show tooltips,
+ *   %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_chooser_get_show_tips (GtkRecentChooser *chooser)
+{
+  gboolean show_tips;
+  
+  g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), FALSE);
+  
+  g_object_get (chooser, "show-tips", &show_tips, NULL);
+  
+  return show_tips;
+}
+
+/**
+ * gtk_recent_chooser_set_show_numbers:
+ * @chooser: a #GtkRecentChooser
+ * @show_private: %TRUE to show numbers, %FALSE otherwise
+ *
+ * Whether to show recently used resources prepended by a unique number.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_set_show_numbers (GtkRecentChooser *chooser,
+                                    gboolean          show_numbers)
+{
+  g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+  
+  g_object_set (chooser, "show-numbers", show_numbers, NULL);
+}
+
+/**
+ * gtk_recent_chooser_get_show_numbers:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Returns whether @chooser should display recently used resources
+ * prepended by a unique number.
+ *
+ * Return value: %TRUE if the recent chooser should show display numbers,
+ *   %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_chooser_get_show_numbers (GtkRecentChooser *chooser)
+{
+  gboolean show_numbers;
+  
+  g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), FALSE);
+  
+  g_object_get (chooser, "show-numbers", &show_numbers, NULL);
+  
+  return show_numbers;
+}
+
+
+/**
+ * gtk_recent_chooser_set_sort_type:
+ * @chooser: a #GtkRecentChooser
+ * @sort_type: sort order that the chooser should use
+ *
+ * Changes the sorting order of the recently used resources list displayed by
+ * @chooser.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_set_sort_type (GtkRecentChooser  *chooser,
+                                 GtkRecentSortType  sort_type)
+{  
+  g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+  
+  g_object_set (chooser, "sort-type", sort_type, NULL);
+}
+
+/**
+ * gtk_recent_chooser_get_sort_type:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Gets the value set by gtk_recent_chooser_set_sort_type().
+ *
+ * Return value: the sorting order of the @chooser.
+ *
+ * Since: 2.10
+ */
+GtkRecentSortType
+gtk_recent_chooser_get_sort_type (GtkRecentChooser *chooser)
+{
+  GtkRecentSortType sort_type;
+  
+  g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), GTK_RECENT_SORT_NONE);
+  
+  g_object_get (chooser, "sort-type", &sort_type, NULL);
+
+  return sort_type;
+}
+
+/**
+ * gtk_recent_chooser_set_sort_func:
+ * @chooser: a #GtkRecentChooser
+ * @sort_func: the comparison function
+ * @sort_data: user data to pass to @sort_func, or %NULL
+ * @destroy_data: destroy notifier for @sort_data, or %NULL
+ *
+ * Sets the comparison function used when sorting to be @sort_func.  If
+ * the @chooser has the sort type set to #GTK_RECENT_SORT_CUSTOM then
+ * the chooser will sort using this function.
+ *
+ * To the comparison function will be passed two #GtkRecentInfo structs and
+ * @sort_data;  @sort_func should return a positive integer if the first
+ * item comes before the second, zero if the two items are equal and
+ * a negative integer if the first item comes after the second.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_set_sort_func  (GtkRecentChooser  *chooser,
+                                  GtkRecentSortFunc  sort_func,
+                                  gpointer           sort_data,
+                                  GDestroyNotify     data_destroy)
+{
+  g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+  
+  GTK_RECENT_CHOOSER_GET_IFACE (chooser)->set_sort_func (chooser,
+                                                        sort_func,
+                                                        sort_data,
+                                                        data_destroy);
+}
+
+/**
+ * gtk_recent_chooser_set_current_uri:
+ * @chooser: a #GtkRecentChooser
+ * @uri: a URI
+ * @error: return location for a #GError, or %NULL
+ *
+ * Sets @uri as the current URI for @chooser.
+ *
+ * Return value: %TRUE if the URI was found.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_chooser_set_current_uri (GtkRecentChooser  *chooser,
+                                   const gchar       *uri,
+                                   GError           **error)
+{
+  g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), FALSE);
+  
+  return GTK_RECENT_CHOOSER_GET_IFACE (chooser)->set_current_uri (chooser, uri, error);
+}
+
+/**
+ * gtk_recent_chooser_get_current_uri:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Gets the URI currently selected by @chooser.
+ *
+ * Return value: a newly allocated string holding a URI.
+ *
+ * Since: 2.10
+ */
+gchar *
+gtk_recent_chooser_get_current_uri (GtkRecentChooser *chooser)
+{
+  g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), NULL);
+  
+  return GTK_RECENT_CHOOSER_GET_IFACE (chooser)->get_current_uri (chooser);
+}
+
+/**
+ * gtk_recent_chooser_get_current_item:
+ * @chooser: a #GtkRecentChooser
+ * 
+ * Gets the #GtkRecentInfo currently selected by @chooser.
+ *
+ * Return value: a #GtkRecentInfo.  Use gtk_recent_info_unref() when
+ *   when you have finished using it.
+ *
+ * Since: 2.10
+ */
+GtkRecentInfo *
+gtk_recent_chooser_get_current_item (GtkRecentChooser *chooser)
+{
+  GtkRecentManager *manager;
+  GtkRecentInfo *retval;
+  gchar *uri;
+  
+  g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), NULL);
+  
+  uri = gtk_recent_chooser_get_current_uri (chooser);
+  if (!uri)
+    return NULL;
+  
+  manager = _gtk_recent_chooser_get_recent_manager (chooser);
+  retval = gtk_recent_manager_lookup_item (manager, uri, NULL);
+  g_free (uri);
+  
+  return retval;
+}
+
+/**
+ * gtk_recent_chooser_select_uri:
+ * @chooser: a #GtkRecentChooser
+ * @uri: a URI
+ * @error: return location for a #GError, or %NULL
+ *
+ * Selects @uri inside @chooser.
+ *
+ * Return value: %TRUE if @uri was found.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_chooser_select_uri (GtkRecentChooser  *chooser,
+                              const gchar       *uri,
+                              GError           **error)
+{
+  g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), FALSE);
+  
+  return GTK_RECENT_CHOOSER_GET_IFACE (chooser)->select_uri (chooser, uri, error);
+}
+
+/**
+ * gtk_recent_chooser_unselect_uri:
+ * @chooser: a #GtkRecentChooser
+ * @uri: a URI
+ *
+ * Unselects @uri inside @chooser.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_unselect_uri (GtkRecentChooser *chooser,
+                                const gchar      *uri)
+{
+  g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+  
+  GTK_RECENT_CHOOSER_GET_IFACE (chooser)->unselect_uri (chooser, uri);
+}
+
+/**
+ * gtk_recent_chooser_select_all:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Selects all the items inside @chooser, if the @chooser supports
+ * multiple selection.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_select_all (GtkRecentChooser *chooser)
+{
+  g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+  
+  GTK_RECENT_CHOOSER_GET_IFACE (chooser)->select_all (chooser);
+}
+
+/**
+ * gtk_recent_chooser_unselect_all:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Unselects all the items inside @chooser.
+ * 
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_unselect_all (GtkRecentChooser *chooser)
+{
+  g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+  
+  GTK_RECENT_CHOOSER_GET_IFACE (chooser)->unselect_all (chooser);
+}
+
+/**
+ * gtk_recent_chooser_get_items:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Gets the list of recently used resources in form of #GtkRecentInfo objects.
+ *
+ * The return value of this function is affected by the "sort-type" and
+ * "limit" properties of @chooser.
+ *
+ * Return value: A newly allocated list of #GtkRecentInfo objects.  You should
+ *   use gtk_recent_info_unref() on every item of the list, and then free
+ *   the list itself using g_list_free().
+ *
+ * Since: 2.10
+ */
+GList *
+gtk_recent_chooser_get_items (GtkRecentChooser *chooser)
+{
+  g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), NULL);
+  
+  return GTK_RECENT_CHOOSER_GET_IFACE (chooser)->get_items (chooser);
+}
+
+/**
+ * gtk_recent_chooser_get_uris:
+ * @chooser: a #GtkRecentChooser
+ * @length: return location for a the length of the URI list, or %NULL
+ *
+ * Gets the URI of the recently used resources.
+ *
+ * The return value of this function is affected by the "sort-type" and "limit"
+ * properties of @chooser.
+ *
+ * Since the returned array is %NULL terminated, @length may be %NULL.
+ * 
+ * Return value: A newly allocated, %NULL terminated array of strings. Use
+ *   g_strfreev() to free it.
+ *
+ * Since: 2.10
+ */
+gchar **
+gtk_recent_chooser_get_uris (GtkRecentChooser *chooser,
+                             gsize            *length)
+{
+  GList *items, *l;
+  gchar **retval;
+  gsize n_items, i;
+  
+  items = gtk_recent_chooser_get_items (chooser);
+  if (!items)
+    return NULL;
+  
+  n_items = g_list_length (items);
+  retval = g_new0 (gchar *, n_items + 1);
+  
+  for (l = items, i = 0; l != NULL; l = l->next)
+    {
+      GtkRecentInfo *info = (GtkRecentInfo *) l->data;
+      const gchar *uri;
+      
+      g_assert (info != NULL);
+      
+      uri = gtk_recent_info_get_uri (info);
+      g_assert (uri != NULL);
+      
+      retval[i++] = g_strdup (uri);
+    }
+  retval[i] = NULL;
+  
+  if (length)
+    *length = i;
+  
+  g_list_foreach (items,
+                 (GFunc) gtk_recent_info_unref,
+                 NULL);
+  g_list_free (items);
+  
+  return retval;
+}
+
+/**
+ * gtk_recent_chooser_add_filter:
+ * @chooser: a #GtkRecentChooser
+ * @filter: a #GtkRecentFilter
+ *
+ * Adds @filter to the list of #GtkRecentFilter objects held by @chooser.
+ *
+ * If no previous filter objects were defined, this function will call
+ * gtk_recent_chooser_set_filter().
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_add_filter (GtkRecentChooser *chooser,
+                              GtkRecentFilter  *filter)
+{
+  g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+  
+  GTK_RECENT_CHOOSER_GET_IFACE (chooser)->add_filter (chooser, filter);
+}
+
+/**
+ * gtk_recent_chooser_remove_filter:
+ * @chooser: a #GtkRecentChooser
+ * @filter: a #GtkRecentFilter
+ *
+ * Removes @filter from the list of #GtkRecentFilter objects held by @chooser.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_remove_filter (GtkRecentChooser *chooser,
+                                 GtkRecentFilter  *filter)
+{
+  g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+  
+  GTK_RECENT_CHOOSER_GET_IFACE (chooser)->remove_filter (chooser, filter);
+}
+
+/**
+ * gtk_recent_chooser_list_filters:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Gets the #GtkRecentFilter objects held by @chooser.
+ *
+ * Return value: A singly linked list of #GtkRecentFilter objects.  You
+ *   should just free the returned list using g_slist_free().
+ *
+ * Since: 2.10
+ */
+GSList *
+gtk_recent_chooser_list_filters (GtkRecentChooser *chooser)
+{
+  g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), NULL);
+  
+  return GTK_RECENT_CHOOSER_GET_IFACE (chooser)->list_filters (chooser);
+}
+
+/**
+ * gtk_recent_chooser_set_filter:
+ * @chooser: a #GtkRecentChooser
+ * @filter: a #GtkRecentFilter
+ *
+ * Sets @filter as the current #GtkRecentFilter object used by @chooser
+ * to affect the displayed recently used resources.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_set_filter (GtkRecentChooser *chooser,
+                              GtkRecentFilter  *filter)
+{
+  g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+  
+  g_object_set (G_OBJECT (chooser), "filter", filter, NULL);
+}
+
+/**
+ * gtk_recent_chooser_get_filter:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Gets the #GtkRecentFilter object currently used by @chooser to affect
+ * the display of the recently used resources.
+ *
+ * Return value: a #GtkRecentFilter object.
+ *
+ * Since: 2.10
+ */
+GtkRecentFilter *
+gtk_recent_chooser_get_filter (GtkRecentChooser *chooser)
+{
+  GtkRecentFilter *filter;
+  
+  g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), NULL);
+  
+  g_object_get (G_OBJECT (chooser), "filter", &filter, NULL);
+  
+  /* we need this hack because g_object_get() increases the refcount
+   * of the returned object; see also gtk_file_chooser_get_filter()
+   * inside gtkfilechooser.c
+   */
+  if (filter)
+    g_object_unref (filter);
+  
+  return filter;
+}
+
+void
+_gtk_recent_chooser_item_activated (GtkRecentChooser *chooser)
+{
+  g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+  
+  g_signal_emit (chooser, chooser_signals[ITEM_ACTIVATED], 0);
+}
+
+void
+_gtk_recent_chooser_selection_changed (GtkRecentChooser *chooser)
+{
+  g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+
+  g_signal_emit (chooser, chooser_signals[SELECTION_CHANGED], 0);
+}
+
+#define __GTK_RECENT_CHOOSER_H__
+#include "gtkaliasdef.c"
diff --git a/gtk/gtkrecentchooser.h b/gtk/gtkrecentchooser.h
new file mode 100644 (file)
index 0000000..2362667
--- /dev/null
@@ -0,0 +1,186 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentchooser.h - Abstract interface for recent file selectors GUIs
+ *
+ * Copyright (C) 2006, Emmanuele Bassi
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_RECENT_CHOOSER_H__
+#define __GTK_RECENT_CHOOSER_H__
+
+#include <gtk/gtkwidget.h>
+
+#include "gtkrecentmanager.h"
+#include "gtkrecentfilter.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_RECENT_CHOOSER                        (gtk_recent_chooser_get_type ())
+#define GTK_RECENT_CHOOSER(obj)                        (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_RECENT_CHOOSER, GtkRecentChooser))
+#define GTK_IS_RECENT_CHOOSER(obj)             (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_RECENT_CHOOSER))
+#define GTK_RECENT_CHOOSER_GET_IFACE(inst)     (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GTK_TYPE_RECENT_CHOOSER, GtkRecentChooserIface))
+
+/**
+ * GtkRecentSortType:
+ * @GTK_RECENT_SORT_NONE: Do not sort the returned list of recently used
+ *   resources.
+ * @GTK_RECENT_SORT_MRU: Sort the returned list with the most recently used
+ *   items first.
+ * @GTK_RECENT_SORT_LRU: Sort the returned list with the least recently used
+ *   items first.
+ * @GTK_RECENT_SORT_CUSTOM: Sort the returned list using a custom sorting
+ *   function passed using gtk_recent_manager_set_sort_func().
+ *
+ * Used to specify the sorting method to be applyed to the recently
+ * used resource list.
+ **/
+typedef enum
+{
+  GTK_RECENT_SORT_NONE = 0,
+  GTK_RECENT_SORT_MRU,
+  GTK_RECENT_SORT_LRU,
+  GTK_RECENT_SORT_CUSTOM
+} GtkRecentSortType;
+
+typedef gint (*GtkRecentSortFunc) (GtkRecentInfo *a,
+                                  GtkRecentInfo *b,
+                                  gpointer       user_data);
+
+
+typedef struct _GtkRecentChooser      GtkRecentChooser; /* dummy */
+typedef struct _GtkRecentChooserIface GtkRecentChooserIface;
+
+#define GTK_RECENT_CHOOSER_ERROR       (gtk_recent_chooser_error_quark ())
+
+typedef enum
+{
+  GTK_RECENT_CHOOSER_ERROR_NOT_FOUND,
+  GTK_RECENT_CHOOSER_ERROR_INVALID_URI
+} GtkRecentChooserError;
+
+GQuark  gtk_recent_chooser_error_quark (void);
+
+
+struct _GtkRecentChooserIface
+{
+  GTypeInterface base_iface;
+  
+  /*
+   * Methods
+   */
+  gboolean          (* set_current_uri)    (GtkRecentChooser  *chooser,
+                                           const gchar       *uri,
+                                           GError           **error);
+  gchar *           (* get_current_uri)    (GtkRecentChooser  *chooser);
+  gboolean          (* select_uri)         (GtkRecentChooser  *chooser,
+                                           const gchar       *uri,
+                                           GError           **error);
+  void              (* unselect_uri)       (GtkRecentChooser  *chooser,
+                                            const gchar       *uri);
+  void              (* select_all)         (GtkRecentChooser  *chooser);
+  void              (* unselect_all)       (GtkRecentChooser  *chooser);
+  GList *           (* get_items)          (GtkRecentChooser  *chooser);
+  GtkRecentManager *(* get_recent_manager) (GtkRecentChooser  *chooser);
+  void              (* add_filter)         (GtkRecentChooser  *chooser,
+                                           GtkRecentFilter   *filter);
+  void              (* remove_filter)      (GtkRecentChooser  *chooser,
+                                           GtkRecentFilter   *filter);
+  GSList *          (* list_filters)       (GtkRecentChooser  *chooser);
+  void              (* set_sort_func)      (GtkRecentChooser  *chooser,
+                                           GtkRecentSortFunc  sort_func,
+                                           gpointer           data,
+                                           GDestroyNotify     destroy);
+  
+  /*
+   * Signals
+   */
+  void             (* item_activated)     (GtkRecentChooser  *chooser);
+  void             (* selection_changed)  (GtkRecentChooser  *chooser);
+};
+
+GType   gtk_recent_chooser_get_type    (void) G_GNUC_CONST;
+
+/*
+ * Configuration
+ */
+void              gtk_recent_chooser_set_show_private    (GtkRecentChooser  *chooser,
+                                                         gboolean           show_private);
+gboolean          gtk_recent_chooser_get_show_private    (GtkRecentChooser  *chooser);
+void              gtk_recent_chooser_set_show_not_found  (GtkRecentChooser  *chooser,
+                                                         gboolean           show_not_found);
+gboolean          gtk_recent_chooser_get_show_not_found  (GtkRecentChooser  *chooser);
+void              gtk_recent_chooser_set_select_multiple (GtkRecentChooser  *chooser,
+                                                         gboolean           select_multiple);
+gboolean          gtk_recent_chooser_get_select_multiple (GtkRecentChooser  *chooser);
+void              gtk_recent_chooser_set_limit           (GtkRecentChooser  *chooser,
+                                                         gint               limit);
+gint              gtk_recent_chooser_get_limit           (GtkRecentChooser  *chooser);
+void              gtk_recent_chooser_set_local_only      (GtkRecentChooser  *chooser,
+                                                         gboolean           local_only);
+gboolean          gtk_recent_chooser_get_local_only      (GtkRecentChooser  *chooser);
+void              gtk_recent_chooser_set_show_tips       (GtkRecentChooser  *chooser,
+                                                         gboolean           show_tips);
+gboolean          gtk_recent_chooser_get_show_tips       (GtkRecentChooser  *chooser);
+void              gtk_recent_chooser_set_show_numbers    (GtkRecentChooser  *chooser,
+                                                         gboolean           show_numbers);
+gboolean          gtk_recent_chooser_get_show_numbers    (GtkRecentChooser  *chooser);
+void              gtk_recent_chooser_set_show_icons      (GtkRecentChooser  *chooser,
+                                                         gboolean           show_icons);
+gboolean          gtk_recent_chooser_get_show_icons      (GtkRecentChooser  *chooser);
+void              gtk_recent_chooser_set_sort_type       (GtkRecentChooser  *chooser,
+                                                         GtkRecentSortType  sort_type);
+GtkRecentSortType gtk_recent_chooser_get_sort_type       (GtkRecentChooser  *chooser);
+void              gtk_recent_chooser_set_sort_func       (GtkRecentChooser  *chooser,
+                                                         GtkRecentSortFunc  sort_func,
+                                                         gpointer           sort_data,
+                                                         GDestroyNotify     data_destroy);
+
+/*
+ * Items handling
+ */
+gboolean       gtk_recent_chooser_set_current_uri  (GtkRecentChooser  *chooser,
+                                                   const gchar       *uri,
+                                                   GError           **error);
+gchar *        gtk_recent_chooser_get_current_uri  (GtkRecentChooser  *chooser);
+GtkRecentInfo *gtk_recent_chooser_get_current_item (GtkRecentChooser  *chooser);
+gboolean       gtk_recent_chooser_select_uri       (GtkRecentChooser  *chooser,
+                                                   const gchar       *uri,
+                                                   GError           **error);
+void           gtk_recent_chooser_unselect_uri     (GtkRecentChooser  *chooser,
+                                                   const gchar       *uri);
+void           gtk_recent_chooser_select_all       (GtkRecentChooser  *chooser);
+void           gtk_recent_chooser_unselect_all     (GtkRecentChooser  *chooser);
+GList *        gtk_recent_chooser_get_items        (GtkRecentChooser  *chooser);
+gchar **       gtk_recent_chooser_get_uris         (GtkRecentChooser  *chooser,
+                                                   gsize             *length);
+
+/*
+ * Filters
+ */
+void            gtk_recent_chooser_add_filter    (GtkRecentChooser *chooser,
+                                                  GtkRecentFilter  *filter);
+void            gtk_recent_chooser_remove_filter (GtkRecentChooser *chooser,
+                                                  GtkRecentFilter  *filter);
+GSList *        gtk_recent_chooser_list_filters  (GtkRecentChooser *chooser);
+void            gtk_recent_chooser_set_filter    (GtkRecentChooser *chooser,
+                                                  GtkRecentFilter  *filter);
+GtkRecentFilter *gtk_recent_chooser_get_filter    (GtkRecentChooser *chooser);
+
+
+G_END_DECLS
+
+#endif /* __GTK_RECENT_CHOOSER_H__ */
diff --git a/gtk/gtkrecentchooserdefault.c b/gtk/gtkrecentchooserdefault.c
new file mode 100644 (file)
index 0000000..e830e96
--- /dev/null
@@ -0,0 +1,2073 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentchooserdefault.c
+ * Copyright (C) 2005-2006, Emmanuele Bassi
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <gdk/gdkscreen.h>
+
+#include "gtkstock.h"
+#include "gtkicontheme.h"
+#include "gtkiconfactory.h"
+#include "gtksettings.h"
+#include "gtktreeview.h"
+#include "gtkliststore.h"
+#include "gtkbutton.h"
+#include "gtkcelllayout.h"
+#include "gtkcellrendererpixbuf.h"
+#include "gtkcellrenderertext.h"
+#include "gtkcheckmenuitem.h"
+#include "gtkclipboard.h"
+#include "gtkcombobox.h"
+#include "gtkentry.h"
+#include "gtkeventbox.h"
+#include "gtkexpander.h"
+#include "gtkframe.h"
+#include "gtkhbox.h"
+#include "gtkhpaned.h"
+#include "gtkimage.h"
+#include "gtkimagemenuitem.h"
+#include "gtkintl.h"
+#include "gtklabel.h"
+#include "gtkmenuitem.h"
+#include "gtkmessagedialog.h"
+#include "gtkscrolledwindow.h"
+#include "gtkseparatormenuitem.h"
+#include "gtksizegroup.h"
+#include "gtktable.h"
+#include "gtktreeview.h"
+#include "gtktreemodelsort.h"
+#include "gtktreemodelfilter.h"
+#include "gtktreeselection.h"
+#include "gtktreestore.h"
+#include "gtktooltips.h"
+#include "gtktypebuiltins.h"
+#include "gtkvbox.h"
+#include "gtkalias.h"
+
+#include "gtkrecentmanager.h"
+#include "gtkrecentfilter.h"
+#include "gtkrecentchooser.h"
+#include "gtkrecentchooserprivate.h"
+#include "gtkrecentchooserutils.h"
+#include "gtkrecentchooserdefault.h"
+
+\f
+
+struct _GtkRecentChooserDefault
+{
+  GtkVBox parent_instance;
+  
+  GtkRecentManager *manager;
+  gulong manager_changed_id;
+  guint local_manager : 1;
+  
+  gint icon_size;
+
+  /* RecentChooser properties */
+  gint limit;  
+  GtkRecentSortType sort_type;
+  guint show_private : 1;
+  guint show_not_found : 1;
+  guint select_multiple : 1;
+  guint show_numbers : 1;
+  guint show_tips : 1;
+  guint show_icons : 1;
+  guint local_only : 1;
+  
+  GSList *filters;
+  GtkRecentFilter *current_filter;
+  GtkWidget *filter_combo_hbox;
+  GtkWidget *filter_combo;
+  
+  GtkRecentSortFunc sort_func;
+  gpointer sort_data;
+  GDestroyNotify sort_data_destroy;
+
+  GtkTooltips *tooltips;
+
+  GtkIconTheme *icon_theme;
+  
+  GtkWidget *recent_view;
+  GtkListStore *recent_store;
+  GtkTreeModel *recent_store_filter;
+  GtkTreeViewColumn *icon_column;
+  GtkTreeViewColumn *meta_column;
+  GtkCellRenderer *meta_renderer;
+  GtkTreeSelection *selection;
+  
+  GtkWidget *recent_popup_menu;
+  GtkWidget *recent_popup_menu_copy_item;
+  GtkWidget *recent_popup_menu_remove_item;
+  GtkWidget *recent_popup_menu_clear_item;
+  GtkWidget *recent_popup_menu_show_private_item;
+  guint load_id;
+  GList *recent_items;
+  gint n_recent_items;
+  gint loaded_items;
+  guint load_state;
+};
+
+typedef struct _GtkRecentChooserDefaultClass
+{
+  GtkVBoxClass parent_class;
+} GtkRecentChooserDefaultClass;
+
+enum {
+  RECENT_URI_COLUMN,
+  RECENT_DISPLAY_NAME_COLUMN,
+  RECENT_INFO_COLUMN,
+    
+  N_RECENT_COLUMNS
+};
+
+enum {
+  LOAD_EMPTY,    /* initial state: the model is empty */
+  LOAD_PRELOAD,  /* the model is loading and not inserted in the tree yet */
+  LOAD_LOADING,  /* the model is fully loaded but not inserted */
+  LOAD_FINISHED  /* the model is fully loaded and inserted */
+};
+
+enum {
+  TEXT_URI_LIST
+};
+
+/* Target types for DnD from the file list */
+static const GtkTargetEntry recent_list_source_targets[] = {
+  { "text/uri-list", 0, TEXT_URI_LIST }
+};
+
+static const int num_recent_list_source_targets = (sizeof (recent_list_source_targets)
+                                                   / sizeof (recent_list_source_targets[0]));
+
+/* Icon size for if we can't get it from the theme */
+#define FALLBACK_ICON_SIZE  48
+#define FALLBACK_ITEM_LIMIT 20
+
+#define NUM_CHARS 40
+#define NUM_LINES 9
+
+\f
+
+/* GObject */
+static void     gtk_recent_chooser_default_class_init   (GtkRecentChooserDefaultClass *klass);
+static void     gtk_recent_chooser_default_init         (GtkRecentChooserDefault      *impl);
+static GObject *gtk_recent_chooser_default_constructor  (GType                         type,
+                                                        guint                         n_construct_prop,
+                                                        GObjectConstructParam        *construct_params);
+static void     gtk_recent_chooser_default_finalize     (GObject                      *object);
+static void     gtk_recent_chooser_default_set_property (GObject                      *object,
+                                                        guint                         prop_id,
+                                                        const GValue                 *value,
+                                                        GParamSpec                   *pspec);
+static void     gtk_recent_chooser_default_get_property (GObject                      *object,
+                                                        guint                         prop_id,
+                                                        GValue                       *value,
+                                                        GParamSpec                   *pspec);
+
+/* GtkRecentChooserIface */
+static void              gtk_recent_chooser_iface_init                 (GtkRecentChooserIface  *iface);
+static gboolean          gtk_recent_chooser_default_set_current_uri    (GtkRecentChooser       *chooser,
+                                                                       const gchar            *uri,
+                                                                       GError                **error);
+static gchar *           gtk_recent_chooser_default_get_current_uri    (GtkRecentChooser       *chooser);
+static gboolean          gtk_recent_chooser_default_select_uri         (GtkRecentChooser       *chooser,
+                                                                       const gchar            *uri,
+                                                                       GError                **error);
+static void              gtk_recent_chooser_default_unselect_uri       (GtkRecentChooser       *chooser,
+                                                                       const gchar            *uri);
+static void              gtk_recent_chooser_default_select_all         (GtkRecentChooser       *chooser);
+static void              gtk_recent_chooser_default_unselect_all       (GtkRecentChooser       *chooser);
+static GList *           gtk_recent_chooser_default_get_items          (GtkRecentChooser       *chooser);
+static GtkRecentManager *gtk_recent_chooser_default_get_recent_manager (GtkRecentChooser       *chooser);
+static void              gtk_recent_chooser_default_set_sort_func      (GtkRecentChooser       *chooser,
+                                                                       GtkRecentSortFunc       sort_func,
+                                                                       gpointer                sort_data,
+                                                                       GDestroyNotify          data_destroy);
+static void              gtk_recent_chooser_default_add_filter         (GtkRecentChooser       *chooser,
+                                                                       GtkRecentFilter        *filter);
+static void              gtk_recent_chooser_default_remove_filter      (GtkRecentChooser       *chooser,
+                                                                       GtkRecentFilter        *filter);
+static GSList *          gtk_recent_chooser_default_list_filters       (GtkRecentChooser       *chooser);
+
+
+static void gtk_recent_chooser_default_map      (GtkWidget *widget);
+static void gtk_recent_chooser_default_show_all (GtkWidget *widget);
+
+static void set_current_filter        (GtkRecentChooserDefault *impl,
+                                      GtkRecentFilter         *filter);
+
+static GtkIconTheme *get_icon_theme_for_widget (GtkWidget   *widget);
+static gint          get_icon_size_for_widget  (GtkWidget   *widget,
+                                               GtkIconSize  icon_size);
+
+static void reload_recent_items (GtkRecentChooserDefault *impl);
+static void chooser_set_model   (GtkRecentChooserDefault *impl);
+
+static void set_recent_manager (GtkRecentChooserDefault *impl,
+                               GtkRecentManager        *manager);
+
+static void chooser_set_sort_type (GtkRecentChooserDefault *impl,
+                                  GtkRecentSortType        sort_type);
+
+static gboolean recent_store_filter_func (GtkTreeModel *model,
+                                         GtkTreeIter  *iter,
+                                         gpointer      user_data);
+
+static void recent_manager_changed_cb (GtkRecentManager  *manager,
+                                      gpointer           user_data);
+static void recent_icon_data_func     (GtkTreeViewColumn *tree_column,
+                                      GtkCellRenderer   *cell,
+                                      GtkTreeModel      *model,
+                                      GtkTreeIter       *iter,
+                                      gpointer           user_data);
+static void recent_meta_data_func     (GtkTreeViewColumn *tree_column,
+                                      GtkCellRenderer   *cell,
+                                      GtkTreeModel      *model,
+                                      GtkTreeIter       *iter,
+                                      gpointer           user_data);
+
+static void selection_changed_cb      (GtkTreeSelection  *z,
+                                      gpointer           user_data);
+static void row_activated_cb          (GtkTreeView       *tree_view,
+                                      GtkTreePath       *tree_path,
+                                      GtkTreeViewColumn *tree_column,
+                                      gpointer           user_data);
+static void filter_combo_changed_cb   (GtkComboBox       *combo_box,
+                                      gpointer           user_data);
+
+static void remove_all_activated_cb   (GtkMenuItem       *menu_item,
+                                      gpointer           user_data);
+static void remove_item_activated_cb  (GtkMenuItem       *menu_item,
+                                      gpointer           user_data);
+static void show_private_toggled_cb   (GtkCheckMenuItem  *menu_item,
+                                      gpointer           user_data);
+
+static gboolean recent_view_popup_menu_cb   (GtkWidget      *widget,
+                                            gpointer        user_data);
+static gboolean recent_view_button_press_cb (GtkWidget      *widget,
+                                            GdkEventButton *event,
+                                            gpointer        user_data);
+
+static void     recent_view_drag_begin_cb         (GtkWidget        *widget,
+                                                  GdkDragContext   *context,
+                                                  gpointer          user_data);
+static void     recent_view_drag_data_get_cb      (GtkWidget        *widget,
+                                                  GdkDragContext   *context,
+                                                  GtkSelectionData *selection_data,
+                                                  guint             info,
+                                                  guint32           time_,
+                                                  gpointer          data);
+
+G_DEFINE_TYPE_WITH_CODE (GtkRecentChooserDefault,
+                        gtk_recent_chooser_default,
+                        GTK_TYPE_VBOX,
+                        G_IMPLEMENT_INTERFACE (GTK_TYPE_RECENT_CHOOSER,
+                                               gtk_recent_chooser_iface_init));
+
+
+\f
+
+static void
+gtk_recent_chooser_iface_init (GtkRecentChooserIface *iface)
+{
+  iface->set_current_uri = gtk_recent_chooser_default_set_current_uri;
+  iface->get_current_uri = gtk_recent_chooser_default_get_current_uri;
+  iface->select_uri = gtk_recent_chooser_default_select_uri;
+  iface->unselect_uri = gtk_recent_chooser_default_unselect_uri;
+  iface->select_all = gtk_recent_chooser_default_select_all;
+  iface->unselect_all = gtk_recent_chooser_default_unselect_all;
+  iface->get_items = gtk_recent_chooser_default_get_items;
+  iface->get_recent_manager = gtk_recent_chooser_default_get_recent_manager;
+  iface->set_sort_func = gtk_recent_chooser_default_set_sort_func;
+  iface->add_filter = gtk_recent_chooser_default_add_filter;
+  iface->remove_filter = gtk_recent_chooser_default_remove_filter;
+  iface->list_filters = gtk_recent_chooser_default_list_filters;
+}
+
+static void
+gtk_recent_chooser_default_class_init (GtkRecentChooserDefaultClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  gobject_class->constructor = gtk_recent_chooser_default_constructor;  
+  gobject_class->finalize = gtk_recent_chooser_default_finalize;
+  gobject_class->set_property = gtk_recent_chooser_default_set_property;
+  gobject_class->get_property = gtk_recent_chooser_default_get_property;
+  
+  widget_class->map = gtk_recent_chooser_default_map;
+  widget_class->show_all = gtk_recent_chooser_default_show_all;
+  
+  _gtk_recent_chooser_install_properties (gobject_class);
+}
+
+static void
+gtk_recent_chooser_default_init (GtkRecentChooserDefault *impl)
+{
+  gtk_box_set_spacing (GTK_BOX (impl), 12);
+
+  /* by default, we use the global manager */
+  impl->local_manager = FALSE;
+  
+  impl->limit = FALLBACK_ITEM_LIMIT;
+  impl->sort_type = GTK_RECENT_SORT_NONE;
+
+  impl->show_icons = TRUE;
+  impl->show_private = FALSE;
+  impl->show_not_found = FALSE;
+  impl->show_tips = TRUE;
+  impl->select_multiple = FALSE;
+  impl->local_only = TRUE;
+  
+  impl->icon_size = FALLBACK_ICON_SIZE;
+  impl->icon_theme = NULL;
+  
+  impl->current_filter = NULL;
+
+  impl->tooltips = gtk_tooltips_new ();
+  g_object_ref_sink (impl->tooltips);
+  
+  impl->recent_items = NULL;
+  impl->n_recent_items = 0;
+  impl->loaded_items = 0;
+  
+  impl->load_state = LOAD_EMPTY;
+}
+
+static GObject *
+gtk_recent_chooser_default_constructor (GType                  type,
+                                       guint                  n_construct_prop,
+                                       GObjectConstructParam *construct_params)
+{
+  GtkRecentChooserDefault *impl;
+  GObject *object;
+  
+  GtkWidget *scrollw;
+  GtkCellRenderer *renderer;
+  
+  object = G_OBJECT_CLASS (gtk_recent_chooser_default_parent_class)->constructor (type, n_construct_prop, construct_params);
+
+  impl = GTK_RECENT_CHOOSER_DEFAULT (object);
+  
+  g_assert (impl->manager);
+  
+  gtk_widget_push_composite_child ();
+  
+  scrollw = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrollw),
+                                      GTK_SHADOW_IN);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollw),
+                                 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+  gtk_box_pack_start (GTK_BOX (impl), scrollw, TRUE, TRUE, 0);
+  gtk_widget_show (scrollw);
+  
+  impl->recent_view = gtk_tree_view_new ();
+  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (impl->recent_view), FALSE);
+  g_signal_connect (impl->recent_view, "row-activated",
+                    G_CALLBACK (row_activated_cb), impl);
+  g_signal_connect (impl->recent_view, "popup-menu",
+                   G_CALLBACK (recent_view_popup_menu_cb), impl);
+  g_signal_connect (impl->recent_view, "button-press-event",
+                   G_CALLBACK (recent_view_button_press_cb), impl);
+  g_signal_connect (impl->recent_view, "drag-begin",
+                   G_CALLBACK (recent_view_drag_begin_cb), impl);
+  g_signal_connect (impl->recent_view, "drag-data-get",
+                   G_CALLBACK (recent_view_drag_data_get_cb), impl);
+
+  g_object_set_data (G_OBJECT (impl->recent_view), "GtkRecentChooserDefault", impl);
+  
+  gtk_container_add (GTK_CONTAINER (scrollw), impl->recent_view);
+  gtk_widget_show (impl->recent_view);
+  
+  impl->icon_column = gtk_tree_view_column_new ();
+  gtk_tree_view_column_set_expand (impl->icon_column, FALSE);
+  gtk_tree_view_column_set_resizable (impl->icon_column, FALSE);
+  
+  renderer = gtk_cell_renderer_pixbuf_new ();
+  gtk_tree_view_column_pack_start (impl->icon_column, renderer, FALSE);
+  gtk_tree_view_column_set_cell_data_func (impl->icon_column,
+                                          renderer,
+                                          recent_icon_data_func,
+                                          impl,
+                                          NULL);
+  gtk_tree_view_append_column (GTK_TREE_VIEW (impl->recent_view),
+                               impl->icon_column);
+  
+  impl->meta_column = gtk_tree_view_column_new ();
+  gtk_tree_view_column_set_expand (impl->meta_column, TRUE);
+  gtk_tree_view_column_set_resizable (impl->meta_column, FALSE);
+  
+  impl->meta_renderer = gtk_cell_renderer_text_new ();
+  g_object_set (G_OBJECT (impl->meta_renderer),
+                "ellipsize", PANGO_ELLIPSIZE_END,
+                NULL);
+  gtk_tree_view_column_pack_start (impl->meta_column, impl->meta_renderer, TRUE);
+  gtk_tree_view_column_set_cell_data_func (impl->meta_column,
+                                          impl->meta_renderer,
+                                          recent_meta_data_func,
+                                          impl,
+                                          NULL);
+  gtk_tree_view_append_column (GTK_TREE_VIEW (impl->recent_view),
+                               impl->meta_column);
+  
+  impl->selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->recent_view));
+  gtk_tree_selection_set_mode (impl->selection, GTK_SELECTION_SINGLE);
+  g_signal_connect (impl->selection, "changed", G_CALLBACK (selection_changed_cb), impl);
+
+  /* drag and drop */
+  gtk_drag_source_set (impl->recent_view,
+                      GDK_BUTTON1_MASK,
+                      recent_list_source_targets,
+                      num_recent_list_source_targets,
+                      GDK_ACTION_COPY);
+
+  impl->filter_combo_hbox = gtk_hbox_new (FALSE, 12);
+  
+  impl->filter_combo = gtk_combo_box_new_text ();
+  gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (impl->filter_combo), FALSE);
+  g_signal_connect (impl->filter_combo, "changed",
+                    G_CALLBACK (filter_combo_changed_cb), impl);
+  gtk_tooltips_set_tip (impl->tooltips,
+                       impl->filter_combo,
+                       _("Select which type of documents are shown"),
+                       NULL);
+  
+  gtk_box_pack_end (GTK_BOX (impl->filter_combo_hbox),
+                    impl->filter_combo,
+                    FALSE, FALSE, 0);
+  gtk_widget_show (impl->filter_combo);
+  
+  gtk_box_pack_end (GTK_BOX (impl), impl->filter_combo_hbox, FALSE, FALSE, 0);
+  
+  gtk_widget_pop_composite_child ();
+  
+  impl->recent_store = gtk_list_store_new (N_RECENT_COLUMNS,
+                                          G_TYPE_STRING,       /* uri */
+                                          G_TYPE_STRING,       /* display_name */
+                                          GTK_TYPE_RECENT_INFO /* info */);
+  
+  return object;
+}
+
+static void
+gtk_recent_chooser_default_set_property (GObject      *object,
+                                        guint         prop_id,
+                                        const GValue *value,
+                                        GParamSpec   *pspec)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (object);
+  
+  switch (prop_id)
+    {
+    case GTK_RECENT_CHOOSER_PROP_RECENT_MANAGER:
+      set_recent_manager (impl, g_value_get_object (value));
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE:
+      impl->show_private = g_value_get_boolean (value);
+      
+      if (impl->recent_store && impl->recent_store_filter)
+        gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->recent_store_filter));
+
+      if (impl->recent_popup_menu_show_private_item)
+       {
+          GtkCheckMenuItem *item = GTK_CHECK_MENU_ITEM (impl->recent_popup_menu_show_private_item);
+         g_signal_handlers_block_by_func (item, G_CALLBACK (show_private_toggled_cb), impl);
+          gtk_check_menu_item_set_active (item, impl->show_private);
+         g_signal_handlers_unblock_by_func (item, G_CALLBACK (show_private_toggled_cb), impl);
+        }
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND:
+      impl->show_not_found = g_value_get_boolean (value);
+      
+      if (impl->recent_store && impl->recent_store_filter)
+        gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->recent_store_filter));
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_TIPS:
+      impl->show_tips = g_value_get_boolean (value);
+
+      if (impl->show_tips)
+        gtk_tooltips_enable (impl->tooltips);
+      else
+        gtk_tooltips_disable (impl->tooltips);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_ICONS:
+      impl->show_icons = g_value_get_boolean (value);
+      gtk_tree_view_column_set_visible (impl->icon_column, impl->show_icons);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE:
+      impl->select_multiple = g_value_get_boolean (value);
+      
+      if (impl->select_multiple)
+        gtk_tree_selection_set_mode (impl->selection, GTK_SELECTION_MULTIPLE);
+      else
+        gtk_tree_selection_set_mode (impl->selection, GTK_SELECTION_SINGLE);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY:
+      impl->local_only = g_value_get_boolean (value);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_LIMIT:
+      impl->limit = g_value_get_int (value);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SORT_TYPE:
+      chooser_set_sort_type (impl, g_value_get_enum (value));
+      break;
+    case GTK_RECENT_CHOOSER_PROP_FILTER:
+      set_current_filter (impl, g_value_get_object (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_recent_chooser_default_get_property (GObject    *object,
+                                        guint       prop_id,
+                                        GValue     *value,
+                                        GParamSpec *pspec)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (object);
+  
+  switch (prop_id)
+    {
+    case GTK_RECENT_CHOOSER_PROP_LIMIT:
+      g_value_set_int (value, impl->limit);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SORT_TYPE:
+      g_value_set_enum (value, impl->sort_type);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE:
+      g_value_set_boolean (value, impl->show_private);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_ICONS:
+      g_value_set_boolean (value, impl->show_icons);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND:
+      g_value_set_boolean (value, impl->show_not_found);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_TIPS:
+      g_value_set_boolean (value, impl->show_tips);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY:
+      g_value_set_boolean (value, impl->local_only);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE:
+      g_value_set_boolean (value, impl->select_multiple);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_FILTER:
+      g_value_set_object (value, impl->current_filter);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_recent_chooser_default_finalize (GObject *object)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (object);
+
+  if (impl->recent_items)
+    {
+      g_list_foreach (impl->recent_items,
+                     (GFunc) gtk_recent_info_unref,
+                     NULL);
+      g_list_free (impl->recent_items);
+      impl->recent_items = NULL;
+    }
+
+  if (impl->manager_changed_id)
+    {
+      g_signal_handler_disconnect (impl->manager, impl->manager_changed_id);
+      impl->manager_changed_id = 0;
+    }
+
+  impl->manager = NULL; 
+  
+  if (impl->sort_data_destroy)
+    {
+      impl->sort_data_destroy (impl->sort_data);
+      
+      impl->sort_data_destroy = NULL;
+      impl->sort_data = NULL;
+      impl->sort_func = NULL;
+    }
+    
+  if (impl->filters)
+    {
+      g_slist_foreach (impl->filters,
+                      (GFunc) g_object_unref,
+                      NULL);
+      g_slist_free (impl->filters);    
+    }
+  
+  if (impl->current_filter)
+    g_object_unref (impl->current_filter);
+
+  if (impl->recent_store_filter)
+    g_object_unref (impl->recent_store_filter);
+
+  if (impl->recent_store)
+    g_object_unref (impl->recent_store);
+
+  if (impl->tooltips)
+    g_object_unref (impl->tooltips);
+  
+  G_OBJECT_CLASS (gtk_recent_chooser_default_parent_class)->finalize (object);
+}
+
+/* override GtkWidget::show_all since we have internal widgets we wish to keep
+ * hidden unless we decide otherwise, like the filter combo box.
+ */
+static void
+gtk_recent_chooser_default_show_all (GtkWidget *widget)
+{
+  gtk_widget_show (widget);
+}
+
+
+
+/* Shows an error dialog set as transient for the specified window */
+static void
+error_message_with_parent (GtkWindow   *parent,
+                          const gchar *msg,
+                          const gchar *detail)
+{
+  GtkWidget *dialog;
+
+  dialog = gtk_message_dialog_new (parent,
+                                  GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+                                  GTK_MESSAGE_ERROR,
+                                  GTK_BUTTONS_OK,
+                                  "%s",
+                                  msg);
+  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+                                           "%s", detail);
+
+  if (parent->group)
+    gtk_window_group_add_window (parent->group, GTK_WINDOW (dialog));
+
+  gtk_dialog_run (GTK_DIALOG (dialog));
+  gtk_widget_destroy (dialog);
+}
+
+/* Returns a toplevel GtkWindow, or NULL if none */
+static GtkWindow *
+get_toplevel (GtkWidget *widget)
+{
+  GtkWidget *toplevel;
+
+  toplevel = gtk_widget_get_toplevel (widget);
+  if (!GTK_WIDGET_TOPLEVEL (toplevel))
+    return NULL;
+  else
+    return GTK_WINDOW (toplevel);
+}
+
+/* Shows an error dialog for the file chooser */
+static void
+error_message (GtkRecentChooserDefault *impl,
+              const gchar             *msg,
+              const gchar             *detail)
+{
+  error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg, detail);
+}
+
+static void
+set_busy_cursor (GtkRecentChooserDefault *impl,
+                gboolean                 show_busy_cursor)
+{
+  GtkWindow *toplevel;
+  GdkDisplay *display;
+  GdkCursor *cursor;
+
+  toplevel = get_toplevel (GTK_WIDGET (impl));
+  if (!toplevel || !GTK_WIDGET_REALIZED (toplevel))
+    return;
+  
+  display = gtk_widget_get_display (GTK_WIDGET (toplevel));
+  
+  cursor = NULL;
+  if (show_busy_cursor)
+    cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
+
+  gdk_window_set_cursor (GTK_WIDGET (toplevel)->window, cursor);
+  gdk_display_flush (display);
+
+  if (cursor)
+    gdk_cursor_unref (cursor);
+}
+
+static void
+chooser_set_model (GtkRecentChooserDefault *impl)
+{
+  g_assert (impl->recent_store != NULL);
+  g_assert (impl->recent_store_filter == NULL);
+  g_assert (impl->load_state == LOAD_LOADING);
+  
+  impl->recent_store_filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (impl->recent_store), NULL);
+  gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->recent_store_filter),
+                                         recent_store_filter_func,
+                                         impl,
+                                         NULL);
+  
+  gtk_tree_view_set_model (GTK_TREE_VIEW (impl->recent_view),
+                          impl->recent_store_filter);
+  gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->recent_view));
+  gtk_tree_view_set_enable_search (GTK_TREE_VIEW (impl->recent_view), TRUE);
+  gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->recent_view),
+                                  RECENT_DISPLAY_NAME_COLUMN);
+
+  impl->load_state = LOAD_FINISHED;
+}
+
+static gboolean
+load_recent_items (gpointer user_data)
+{
+  GtkRecentChooserDefault *impl;
+  GtkRecentInfo *info;
+  GtkTreeIter iter;
+  const gchar *uri, *name;
+  gboolean retval;
+  
+  GDK_THREADS_ENTER ();
+  
+  impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+  
+  g_assert ((impl->load_state == LOAD_EMPTY) ||
+            (impl->load_state == LOAD_PRELOAD));
+  
+  /* store the items for multiple runs */
+  if (!impl->recent_items)
+    {
+      impl->recent_items = gtk_recent_chooser_get_items (GTK_RECENT_CHOOSER (impl));
+      if (!impl->recent_items)
+        {
+          GDK_THREADS_LEAVE ();
+
+         impl->load_state = LOAD_FINISHED;
+          
+          return FALSE;
+        }
+        
+      impl->n_recent_items = g_list_length (impl->recent_items);
+      impl->loaded_items = 0;
+      impl->load_state = LOAD_PRELOAD;
+    }
+  
+  info = (GtkRecentInfo *) g_list_nth_data (impl->recent_items,
+                                            impl->loaded_items);
+  g_assert (info);
+
+  uri = gtk_recent_info_get_uri (info);
+  name = gtk_recent_info_get_display_name (info);
+  
+  /* at this point, everything goes inside the model; operations on the
+   * visualization of items inside the model are done in the cell data
+   * funcs (remember that there are two of those: one for the icon and
+   * one for the text), while the filtering is done only when a filter
+   * is actually loaded. */
+  gtk_list_store_append (impl->recent_store, &iter);
+  gtk_list_store_set (impl->recent_store, &iter,
+                     RECENT_URI_COLUMN, uri,           /* uri  */
+                     RECENT_DISPLAY_NAME_COLUMN, name, /* display_name */
+                     RECENT_INFO_COLUMN, info,         /* info */
+                     -1);
+  
+  impl->loaded_items += 1;
+
+  if (impl->loaded_items == impl->n_recent_items)
+    {
+      /* we have finished loading, so we remove the items cache */
+      impl->load_state = LOAD_LOADING;
+      
+      g_list_foreach (impl->recent_items,
+                     (GFunc) gtk_recent_info_unref,
+                     NULL);
+      g_list_free (impl->recent_items);
+      
+      impl->recent_items = NULL;
+      impl->n_recent_items = 0;
+      impl->loaded_items = 0;
+      
+      if (impl->recent_store_filter)
+        {
+          g_object_unref (impl->recent_store_filter);
+         impl->recent_store_filter = NULL;
+       }
+      
+      /* load the filled up model */
+      chooser_set_model (impl);
+
+      retval = FALSE;
+    }
+  else
+    {
+      /* we did not finish, so continue loading */
+      retval = TRUE;
+    }
+  
+  GDK_THREADS_LEAVE ();
+  
+  return retval;
+}
+
+static void
+cleanup_after_load (gpointer user_data)
+{
+  GtkRecentChooserDefault *impl;
+  
+  GDK_THREADS_ENTER ();
+  
+  impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+
+  if (impl->load_id != 0)
+    {
+      g_assert ((impl->load_state == LOAD_PRELOAD) ||
+               (impl->load_state == LOAD_LOADING) ||
+               (impl->load_state == LOAD_FINISHED));
+      
+      /* we have officialy finished loading all the items,
+       * so we can reset the state machine
+       */
+      g_source_remove (impl->load_id);
+      impl->load_id = 0;
+      impl->load_state = LOAD_EMPTY;
+    }
+  else
+    g_assert ((impl->load_state == LOAD_EMPTY) ||
+             (impl->load_state == LOAD_LOADING) ||
+             (impl->load_state == LOAD_FINISHED));
+
+  set_busy_cursor (impl, FALSE);
+  
+  GDK_THREADS_LEAVE ();
+}
+
+/* clears the current model and reloads the recently used resources */
+static void
+reload_recent_items (GtkRecentChooserDefault *impl)
+{
+  /* reload is already in progress - do not disturb */
+  if (impl->load_id)
+    return;
+  
+  gtk_tree_view_set_model (GTK_TREE_VIEW (impl->recent_view), NULL);
+  gtk_list_store_clear (impl->recent_store);
+  
+  if (!impl->icon_theme)
+    impl->icon_theme = get_icon_theme_for_widget (GTK_WIDGET (impl));
+
+  impl->icon_size = get_icon_size_for_widget (GTK_WIDGET (impl),
+                                             GTK_ICON_SIZE_BUTTON);
+
+  set_busy_cursor (impl, TRUE);
+
+  impl->load_state = LOAD_EMPTY;
+  impl->load_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 30,
+                                  load_recent_items,
+                                  impl,
+                                  cleanup_after_load);
+}
+
+/* taken form gtkfilechooserdialog.c */
+static void
+set_default_size (GtkRecentChooserDefault *impl)
+{
+  GtkWidget *widget;
+  gint width, height;
+  gint font_size;
+  GdkScreen *screen;
+  gint monitor_num;
+  GtkRequisition req;
+  GdkRectangle monitor;
+
+  widget = GTK_WIDGET (impl);
+
+  /* Size based on characters and the icon size */
+  font_size = pango_font_description_get_size (widget->style->font_desc);
+  font_size = PANGO_PIXELS (font_size);
+
+  width = impl->icon_size + font_size * NUM_CHARS;
+  height = (impl->icon_size + font_size) * NUM_LINES;
+
+  /* Use at least the requisition size... */
+  gtk_widget_size_request (widget, &req);
+  width = MAX (width, req.width);
+  height = MAX (height, req.height);
+
+  /* ... but no larger than the monitor */
+  screen = gtk_widget_get_screen (widget);
+  monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window);
+
+  gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+  width = MIN (width, monitor.width * 3 / 4);
+  height = MIN (height, monitor.height * 3 / 4);
+
+  /* Set size */
+  gtk_widget_set_size_request (impl->recent_view, width, height);
+}
+
+static void
+gtk_recent_chooser_default_map (GtkWidget *widget)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (widget);
+  
+  if (GTK_WIDGET_CLASS (gtk_recent_chooser_default_parent_class)->map)
+    GTK_WIDGET_CLASS (gtk_recent_chooser_default_parent_class)->map (widget);
+
+  /* reloads everything */
+  reload_recent_items (impl);
+
+  set_default_size (impl);
+}
+
+static void
+recent_icon_data_func (GtkTreeViewColumn *tree_column,
+                      GtkCellRenderer   *cell,
+                      GtkTreeModel      *model,
+                      GtkTreeIter       *iter,
+                      gpointer           user_data)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+  GtkRecentInfo *info = NULL;
+  GdkPixbuf *pixbuf;
+  
+  gtk_tree_model_get (model, iter,
+                      RECENT_INFO_COLUMN, &info,
+                      -1);
+  g_assert (info != NULL);
+  
+  pixbuf = gtk_recent_info_get_icon (info, impl->icon_size);
+  
+  g_object_set (cell,
+                "pixbuf", pixbuf,
+                NULL);
+  
+  if (pixbuf)  
+    g_object_unref (pixbuf);
+}
+
+static void
+recent_meta_data_func (GtkTreeViewColumn *tree_column,
+                      GtkCellRenderer   *cell,
+                      GtkTreeModel      *model,
+                      GtkTreeIter       *iter,
+                      gpointer           user_data)
+{
+  GtkRecentInfo *info = NULL;
+  gchar *uri;
+  gchar *name;
+  GString *data;
+  
+  data = g_string_new (NULL);
+  
+  gtk_tree_model_get (model, iter,
+                      RECENT_DISPLAY_NAME_COLUMN, &name,
+                      RECENT_INFO_COLUMN, &info,
+                      -1);
+  g_assert (info != NULL);
+  
+  uri = gtk_recent_info_get_uri_display (info);
+  
+  if (!name)
+    name = gtk_recent_info_get_short_name (info);
+  g_string_append_printf (data,
+                         "<b>%s</b>\n"
+                         "<small>Location: %s</small>",
+                         name,
+                         uri);
+  
+  g_object_set (cell,
+                "markup", data->str,
+                "sensitive", gtk_recent_info_exists (info),
+                NULL);
+  
+  g_string_free (data, TRUE);
+  g_free (uri);
+  g_free (name);
+  gtk_recent_info_unref (info);
+}
+
+
+static gchar *
+gtk_recent_chooser_default_get_current_uri (GtkRecentChooser *chooser)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+  
+  g_assert (impl->selection != NULL);
+  
+  if (!impl->select_multiple)
+    {
+      GtkTreeModel *model;
+      GtkTreeIter iter;
+      gchar *uri = NULL;
+      
+      if (!gtk_tree_selection_get_selected (impl->selection, &model, &iter))
+        return NULL;
+      
+      gtk_tree_model_get (model, &iter, RECENT_URI_COLUMN, &uri, -1);
+      
+      return uri;
+    }
+  
+  return NULL;
+}
+
+typedef struct
+{
+  guint found : 1;
+  guint do_select : 1;
+  guint do_activate : 1;
+  
+  gchar *uri;
+  
+  GtkRecentChooserDefault *impl;
+} SelectURIData;
+
+static gboolean
+scan_for_uri_cb (GtkTreeModel *model,
+                GtkTreePath  *path,
+                GtkTreeIter  *iter,
+                gpointer      user_data)
+{
+  SelectURIData *select_data = (SelectURIData *) user_data;
+  gchar *uri;
+  
+  if (!select_data)
+    return TRUE;
+  
+  if (select_data->found)
+    return TRUE;
+  
+  gtk_tree_model_get (model, iter, RECENT_URI_COLUMN, &uri, -1);
+  if (uri && (0 == strcmp (uri, select_data->uri)))
+    {
+      select_data->found = TRUE;
+      
+      if (select_data->do_activate)
+        {
+          gtk_tree_view_row_activated (GTK_TREE_VIEW (select_data->impl->recent_view),
+                                              path,
+                                              select_data->impl->meta_column);
+          
+          return TRUE;
+        }
+      
+      if (select_data->do_select)
+        gtk_tree_selection_select_iter (select_data->impl->selection, iter);
+      else
+        gtk_tree_selection_unselect_iter (select_data->impl->selection, iter);
+      
+      return TRUE;
+    }
+  
+  return FALSE;
+}
+
+static gboolean
+gtk_recent_chooser_default_set_current_uri (GtkRecentChooser  *chooser,
+                                           const gchar       *uri,
+                                           GError           **error)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+  SelectURIData *data;
+  
+  data = g_new0 (SelectURIData, 1);
+  data->uri = g_strdup (uri);
+  data->impl = impl;
+  data->found = FALSE;
+  data->do_activate = TRUE;
+  data->do_select = TRUE;
+  
+  gtk_tree_model_foreach (GTK_TREE_MODEL (impl->recent_store),
+                         scan_for_uri_cb,
+                         data);
+  
+  if (!data->found)
+    {
+      g_free (data->uri);
+      g_free (data);
+      
+      g_set_error (error, GTK_RECENT_CHOOSER_ERROR,
+                  GTK_RECENT_CHOOSER_ERROR_NOT_FOUND,
+                  _("No item for URI '%s' found"),
+                  uri);
+      return FALSE;
+    }
+  
+  g_free (data->uri);
+  g_free (data);
+
+  return TRUE;
+}
+
+static gboolean
+gtk_recent_chooser_default_select_uri (GtkRecentChooser  *chooser,
+                                      const gchar       *uri,
+                                      GError           **error)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+  SelectURIData *data;
+  
+  data = g_new0 (SelectURIData, 1);
+  data->uri = g_strdup (uri);
+  data->impl = impl;
+  data->found = FALSE;
+  data->do_activate = FALSE;
+  data->do_select = TRUE;
+  
+  gtk_tree_model_foreach (GTK_TREE_MODEL (impl->recent_store),
+                         scan_for_uri_cb,
+                         data);
+  
+  if (!data->found)
+    {
+      g_free (data->uri);
+      g_free (data);
+      
+      g_set_error (error, GTK_RECENT_CHOOSER_ERROR,
+                  GTK_RECENT_CHOOSER_ERROR_NOT_FOUND,
+                  _("No item for URI '%s' found"),
+                  uri);
+      return FALSE;
+    }
+  
+  g_free (data->uri);
+  g_free (data);
+
+  return TRUE;
+}
+
+static void
+gtk_recent_chooser_default_unselect_uri (GtkRecentChooser *chooser,
+                                        const gchar      *uri)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+  SelectURIData *data;
+  
+  data = g_new0 (SelectURIData, 1);
+  data->uri = g_strdup (uri);
+  data->impl = impl;
+  data->found = FALSE;
+  data->do_activate = FALSE;
+  data->do_select = FALSE;
+  
+  gtk_tree_model_foreach (GTK_TREE_MODEL (impl->recent_store),
+                         scan_for_uri_cb,
+                         data);
+  
+  g_free (data->uri);
+  g_free (data);
+}
+
+static void
+gtk_recent_chooser_default_select_all (GtkRecentChooser *chooser)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+  
+  if (!impl->select_multiple)
+    return;
+  
+  gtk_tree_selection_select_all (impl->selection);
+}
+
+static void
+gtk_recent_chooser_default_unselect_all (GtkRecentChooser *chooser)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+  
+  gtk_tree_selection_unselect_all (impl->selection);
+}
+
+static void
+gtk_recent_chooser_default_set_sort_func (GtkRecentChooser  *chooser,
+                                         GtkRecentSortFunc  sort_func,
+                                         gpointer           sort_data,
+                                         GDestroyNotify     data_destroy)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+  
+  if (impl->sort_data_destroy)
+    {
+      impl->sort_data_destroy (impl->sort_data);
+      
+      impl->sort_func = NULL;
+      impl->sort_data = NULL;
+      impl->sort_data_destroy = NULL;
+    }
+  
+  if (sort_func)
+    {
+      impl->sort_func = sort_func;
+      impl->sort_data = sort_data;
+      impl->sort_data_destroy = data_destroy;
+    }
+}
+
+static gint
+sort_recent_items_mru (GtkRecentInfo *a,
+                      GtkRecentInfo *b,
+                      gpointer       unused)
+{
+  g_assert (a != NULL && b != NULL);
+  
+  return (gtk_recent_info_get_modified (a) < gtk_recent_info_get_modified (b));
+}
+
+static gint
+sort_recent_items_lru (GtkRecentInfo *a,
+                      GtkRecentInfo *b,
+                      gpointer       unused)
+{
+  g_assert (a != NULL && b != NULL);
+  
+  return (gtk_recent_info_get_modified (a) > gtk_recent_info_get_modified (b));
+}
+
+/* our proxy sorting function */
+static gint
+sort_recent_items_proxy (gpointer *a,
+                        gpointer *b,
+                        gpointer  user_data)
+{
+  GtkRecentInfo *info_a = (GtkRecentInfo *) a;
+  GtkRecentInfo *info_b = (GtkRecentInfo *) b;
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+
+  if (impl->sort_func)
+    return (* impl->sort_func) (info_a,
+                               info_b,
+                               impl->sort_data);
+  
+  /* fallback */
+  return 0;
+}
+
+static void
+chooser_set_sort_type (GtkRecentChooserDefault *impl,
+                      GtkRecentSortType        sort_type)
+{
+  if (impl->sort_type == sort_type)
+    return;
+
+  impl->sort_type = sort_type;
+}
+
+static GList *
+gtk_recent_chooser_default_get_items (GtkRecentChooser *chooser)
+{
+  GtkRecentChooserDefault *impl;
+  gint limit;
+  GtkRecentSortType sort_type;
+  GList *items;
+  GCompareDataFunc compare_func;
+  gint length;
+  
+  impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+  
+  if (!impl->manager)
+    return NULL;
+
+  items = gtk_recent_manager_get_items (impl->manager);
+  if (!items)
+    return NULL;
+  limit = gtk_recent_chooser_get_limit (chooser);
+  sort_type = gtk_recent_chooser_get_sort_type (chooser);
+
+  switch (sort_type)
+    {
+    case GTK_RECENT_SORT_NONE:
+      compare_func = NULL;
+      break;
+    case GTK_RECENT_SORT_MRU:
+      compare_func = (GCompareDataFunc) sort_recent_items_mru;
+      break;
+    case GTK_RECENT_SORT_LRU:
+      compare_func = (GCompareDataFunc) sort_recent_items_lru;
+      break;
+    case GTK_RECENT_SORT_CUSTOM:
+      compare_func = (GCompareDataFunc) sort_recent_items_proxy;
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+  
+  /* sort the items; the filtering will be dealt with using
+   * the treeview's own filter object
+   */
+  if (compare_func)
+    items = g_list_sort_with_data (items, compare_func, impl);
+  
+  length = g_list_length (items);
+  if ((limit != -1) && (length > limit))
+    {
+      GList *clamp, *l;
+      
+      clamp = g_list_nth (items, limit - 1);
+      
+      if (!clamp)
+        return items;
+      
+      l = clamp->next;
+      clamp->next = NULL;
+    
+      g_list_foreach (l, (GFunc) gtk_recent_info_unref, NULL);
+      g_list_free (l);
+    }
+  
+  return items;
+}
+
+static GtkRecentManager *
+gtk_recent_chooser_default_get_recent_manager (GtkRecentChooser *chooser)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+  
+  return impl->manager;
+}
+
+static void
+show_filters (GtkRecentChooserDefault *impl,
+              gboolean                 show)
+{
+  if (show)
+    gtk_widget_show (impl->filter_combo_hbox);
+  else
+    gtk_widget_hide (impl->filter_combo_hbox);
+}
+
+static void
+gtk_recent_chooser_default_add_filter (GtkRecentChooser *chooser,
+                                      GtkRecentFilter  *filter)
+{
+  GtkRecentChooserDefault *impl;
+  const gchar *name;
+
+  impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+  
+  if (g_slist_find (impl->filters, filter))
+    {
+      g_warning ("gtk_recent_chooser_add_filter() called on filter already in list\n");
+      return;
+    }
+  
+  g_object_ref_sink (filter);
+  impl->filters = g_slist_append (impl->filters, filter);
+  
+  /* display new filter */
+  name = gtk_recent_filter_get_name (filter);
+  if (!name)
+    name = "Untitled filter";
+    
+  gtk_combo_box_append_text (GTK_COMBO_BOX (impl->filter_combo), name);
+  
+  if (!g_slist_find (impl->filters, impl->current_filter))
+    set_current_filter (impl, filter);
+  
+  show_filters (impl, TRUE);
+}
+
+static void
+gtk_recent_chooser_default_remove_filter (GtkRecentChooser *chooser,
+                                         GtkRecentFilter  *filter)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+  gint filter_idx;
+  
+  filter_idx = g_slist_index (impl->filters, filter);
+  
+  if (filter_idx < 0)
+    {
+      g_warning ("gtk_recent_chooser_remove_filter() called on filter not in list\n");
+      return;  
+    }
+  
+  impl->filters = g_slist_remove (impl->filters, filter);
+  
+  if (filter == impl->current_filter)
+    {
+      if (impl->filters)
+        set_current_filter (impl, impl->filters->data);
+      else
+        set_current_filter (impl, NULL);
+    }
+  
+  model = gtk_combo_box_get_model (GTK_COMBO_BOX (impl->filter_combo));
+  gtk_tree_model_iter_nth_child (model, &iter, NULL, filter_idx);
+  gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+  
+  g_object_unref (filter);
+  
+  if (!impl->filters)
+    show_filters (impl, FALSE);
+}
+
+static GSList *
+gtk_recent_chooser_default_list_filters (GtkRecentChooser *chooser)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+  
+  return g_slist_copy (impl->filters);
+}
+
+static gboolean
+get_is_recent_filtered (GtkRecentChooserDefault *impl,
+                       GtkRecentInfo           *info)
+{
+  GtkRecentFilter *current_filter;
+  GtkRecentFilterInfo filter_info;
+  GtkRecentFilterFlags needed;
+  gboolean retval;
+
+  g_assert (info != NULL);
+  
+  if (!impl->current_filter)
+    return FALSE;
+  
+  current_filter = impl->current_filter;
+  needed = gtk_recent_filter_get_needed (current_filter);
+  
+  filter_info.contains = GTK_RECENT_FILTER_URI | GTK_RECENT_FILTER_MIME_TYPE;
+  
+  filter_info.uri = gtk_recent_info_get_uri (info);
+  filter_info.mime_type = gtk_recent_info_get_mime_type (info);
+  
+  if (needed & GTK_RECENT_FILTER_DISPLAY_NAME)
+    {
+      filter_info.display_name = gtk_recent_info_get_display_name (info);
+      filter_info.contains |= GTK_RECENT_FILTER_DISPLAY_NAME;
+    }
+  else
+    filter_info.uri = NULL;
+  
+  if (needed & GTK_RECENT_FILTER_APPLICATION)
+    {
+      filter_info.applications = (const gchar **) gtk_recent_info_get_applications (info, NULL);
+      filter_info.contains |= GTK_RECENT_FILTER_APPLICATION;
+    }
+  else
+    filter_info.applications = NULL;
+
+  if (needed & GTK_RECENT_FILTER_GROUP)
+    {
+      filter_info.groups = (const gchar **) gtk_recent_info_get_groups (info, NULL);
+      filter_info.contains |= GTK_RECENT_FILTER_GROUP;
+    }
+  else
+    filter_info.groups = NULL;
+
+  if (needed & GTK_RECENT_FILTER_AGE)
+    {
+      filter_info.age = gtk_recent_info_get_age (info);
+      filter_info.contains |= GTK_RECENT_FILTER_AGE;
+    }
+  else
+    filter_info.age = -1;
+  
+  retval = gtk_recent_filter_filter (current_filter, &filter_info);
+  
+  /* this we own */
+  if (filter_info.applications)
+    g_strfreev ((gchar **) filter_info.applications);
+  
+  return !retval;
+}
+
+static gboolean
+recent_store_filter_func (GtkTreeModel *model,
+                          GtkTreeIter  *iter,
+                          gpointer      user_data)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+  GtkRecentInfo *info = NULL;
+
+  if (!impl->current_filter)
+    return TRUE;
+  
+  gtk_tree_model_get (model, iter,
+                      RECENT_INFO_COLUMN, &info,
+                      -1);
+  if (!info)
+    return TRUE;
+    
+  if (get_is_recent_filtered (impl, info))
+    return FALSE;
+  
+  if (impl->local_only && !gtk_recent_info_is_local (info))
+    return FALSE;
+  
+  if ((!impl->show_private) && gtk_recent_info_get_private_hint (info))
+    return FALSE;
+  
+  if ((!impl->show_not_found) && !gtk_recent_info_exists (info))
+    return FALSE;
+      
+  return TRUE;
+}
+
+static void
+set_current_filter (GtkRecentChooserDefault *impl,
+                   GtkRecentFilter         *filter)
+{
+  if (impl->current_filter != filter)
+    {
+      gint filter_idx;
+      
+      filter_idx = g_slist_index (impl->filters, filter);
+      if (impl->filters && filter && filter_idx < 0)
+        return;
+      
+      if (impl->current_filter)
+        g_object_unref (impl->current_filter);
+      
+      impl->current_filter = filter;
+      
+      if (impl->current_filter)     
+        {
+          g_object_ref_sink (impl->current_filter);
+        }
+      
+      if (impl->filters)
+        gtk_combo_box_set_active (GTK_COMBO_BOX (impl->filter_combo),
+                                  filter_idx);
+      
+      if (impl->recent_store && impl->recent_store_filter)
+        {
+          gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->recent_store_filter));
+        }
+      
+      g_object_notify (G_OBJECT (impl), "filter");
+    }
+}
+
+static GtkIconTheme *
+get_icon_theme_for_widget (GtkWidget *widget)
+{
+  if (gtk_widget_has_screen (widget))
+    return gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
+
+  return gtk_icon_theme_get_default ();
+}
+
+static gint
+get_icon_size_for_widget (GtkWidget   *widget,
+                         GtkIconSize  icon_size)
+{
+  GtkSettings *settings;
+  gint width, height;
+
+  if (gtk_widget_has_screen (widget))
+    settings = gtk_settings_get_for_screen (gtk_widget_get_screen (widget));
+  else
+    settings = gtk_settings_get_default ();
+
+  if (gtk_icon_size_lookup_for_settings (settings, icon_size,
+                                         &width, &height))
+    return MAX (width, height);
+
+  return FALLBACK_ICON_SIZE;
+}
+
+
+static void
+recent_manager_changed_cb (GtkRecentManager *manager,
+                          gpointer          user_data)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+
+  reload_recent_items (impl);
+}
+
+static void
+selection_changed_cb (GtkTreeSelection *selection,
+                     gpointer          user_data)
+{
+  _gtk_recent_chooser_selection_changed (GTK_RECENT_CHOOSER (user_data));
+}
+
+static void
+row_activated_cb (GtkTreeView       *tree_view,
+                 GtkTreePath       *tree_path,
+                 GtkTreeViewColumn *tree_column,
+                 gpointer           user_data)
+{
+  _gtk_recent_chooser_item_activated (GTK_RECENT_CHOOSER (user_data));
+}
+
+static void
+filter_combo_changed_cb (GtkComboBox *combo_box,
+                        gpointer     user_data)
+{
+  GtkRecentChooserDefault *impl;
+  gint new_index;
+  GtkRecentFilter *filter;
+  
+  impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+  
+  new_index = gtk_combo_box_get_active (combo_box);
+  filter = g_slist_nth_data (impl->filters, new_index);
+  
+  set_current_filter (impl, filter);
+}
+
+static GdkPixbuf *
+get_drag_pixbuf (GtkRecentChooserDefault *impl)
+{
+  GtkRecentInfo *info;
+  GdkPixbuf *retval;
+  gint size;
+  
+  g_assert (GTK_IS_RECENT_CHOOSER_DEFAULT (impl));
+
+  info = gtk_recent_chooser_get_current_item (GTK_RECENT_CHOOSER (impl));
+  if (!info)
+    return NULL;
+
+  size = get_icon_size_for_widget (GTK_WIDGET (impl), GTK_ICON_SIZE_DND);
+
+  retval = gtk_recent_info_get_icon (info, size);
+  gtk_recent_info_unref (info);
+
+  return retval;
+}
+
+static void
+recent_view_drag_begin_cb (GtkWidget      *widget,
+                          GdkDragContext *context,
+                          gpointer        user_data)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+  GdkPixbuf *pixbuf;
+
+  pixbuf = get_drag_pixbuf (impl);
+  if (pixbuf)
+    {
+      gtk_drag_set_icon_pixbuf (context, pixbuf, 0, 0);
+      g_object_unref (pixbuf);
+    }
+  else
+    gtk_drag_set_icon_default (context);
+}
+
+typedef struct
+{
+  gchar **uri_list;
+  gsize next_pos;
+} DragData;
+
+static void
+append_uri_to_urilist (GtkTreeModel *model,
+                      GtkTreePath  *path,
+                      GtkTreeIter  *iter,
+                      gpointer      user_data)
+{
+  DragData *drag_data = (DragData *) user_data;
+  GtkTreeModel *child_model;
+  GtkTreeIter child_iter;
+  gchar *uri = NULL;
+  gsize pos;
+
+  child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+  gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model),
+                                                   &child_iter,
+                                                   iter);
+  gtk_tree_model_get (child_model, &child_iter,
+                     RECENT_URI_COLUMN, &uri,
+                     -1);
+  g_assert (uri != NULL);
+
+  pos = drag_data->next_pos;
+  drag_data->uri_list[pos] = g_strdup (uri);
+  drag_data->next_pos = pos + 1;
+}
+
+static void
+recent_view_drag_data_get_cb (GtkWidget        *widget,
+                             GdkDragContext   *context,
+                             GtkSelectionData *selection_data,
+                             guint             info,
+                             guint32           time_,
+                             gpointer          data)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (data);
+  DragData *drag_data;
+  gsize n_uris;
+  
+  n_uris = gtk_tree_selection_count_selected_rows (impl->selection);
+  if (n_uris == 0)
+         return;
+
+  drag_data = g_new (DragData, 1);
+  drag_data->uri_list = g_new0 (gchar *, n_uris + 1);
+  drag_data->next_pos = 0;
+  
+  gtk_tree_selection_selected_foreach (impl->selection,
+                                      append_uri_to_urilist,
+                                      drag_data);
+  
+  gtk_selection_data_set_uris (selection_data, drag_data->uri_list);
+
+  g_strfreev (drag_data->uri_list);
+  g_free (drag_data);
+}
+
+
+
+static void
+remove_selected_from_list (GtkRecentChooserDefault *impl)
+{
+  gchar *uri;
+  GError *err;
+  
+  if (impl->select_multiple)
+    return;
+  
+  uri = gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (impl));
+  if (!uri)
+    return;
+  
+  err = NULL;
+  if (!gtk_recent_manager_remove_item (impl->manager, uri, &err))
+    {
+      gchar *msg;
+   
+      msg = strdup (_("Could not remove item"));
+      error_message (impl, msg, err->message);
+      
+      g_free (msg);
+      g_error_free (err);
+    }
+  
+  g_free (uri);
+}
+
+static void
+copy_activated_cb (GtkMenuItem *menu_item,
+                  gpointer     user_data)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+  GtkRecentInfo *info;
+  gchar *utf8_uri;
+
+  info = gtk_recent_chooser_get_current_item (GTK_RECENT_CHOOSER (impl));
+  if (!info)
+    return;
+
+  utf8_uri = gtk_recent_info_get_uri_display (info);
+  
+  gtk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (impl),
+                                                   GDK_SELECTION_CLIPBOARD),
+                          utf8_uri, -1);
+
+  g_free (utf8_uri);
+}
+
+static void
+remove_all_activated_cb (GtkMenuItem *menu_item,
+                        gpointer     user_data)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+  GError *err = NULL;
+  
+  gtk_recent_manager_purge_items (impl->manager, &err);
+  if (err)
+    {
+       gchar *msg;
+
+       msg = g_strdup (_("Could not clear list"));
+
+       error_message (impl, msg, err->message);
+       
+       g_free (msg);
+       g_error_free (err);
+    }
+}
+
+static void
+remove_item_activated_cb (GtkMenuItem *menu_item,
+                         gpointer     user_data)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+  
+  remove_selected_from_list (impl);
+}
+
+static void
+show_private_toggled_cb (GtkCheckMenuItem *menu_item,
+                        gpointer          user_data)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+  
+  g_object_set (G_OBJECT (impl),
+               "show-private", gtk_check_menu_item_get_active (menu_item),
+               NULL);
+}
+
+static void
+recent_popup_menu_detach_cb (GtkWidget *attach_widget,
+                            GtkMenu   *menu)
+{
+  GtkRecentChooserDefault *impl;
+  
+  impl = g_object_get_data (G_OBJECT (attach_widget), "GtkRecentChooserDefault");
+  g_assert (GTK_IS_RECENT_CHOOSER_DEFAULT (impl));
+  
+  impl->recent_popup_menu = NULL;
+  impl->recent_popup_menu_remove_item = NULL;
+  impl->recent_popup_menu_copy_item = NULL;
+  impl->recent_popup_menu_clear_item = NULL;
+  impl->recent_popup_menu_show_private_item = NULL;
+}
+
+static void
+recent_view_menu_ensure_state (GtkRecentChooserDefault *impl)
+{
+  gint count;
+  
+  g_assert (GTK_IS_RECENT_CHOOSER_DEFAULT (impl));
+  g_assert (impl->recent_popup_menu != NULL);
+
+  if (!impl->manager)
+    count = 0;
+  else
+    g_object_get (G_OBJECT (impl->manager), "size", &count, NULL);
+
+  if (count == 0)
+    {
+      gtk_widget_set_sensitive (impl->recent_popup_menu_remove_item, FALSE);
+      gtk_widget_set_sensitive (impl->recent_popup_menu_copy_item, FALSE);
+      gtk_widget_set_sensitive (impl->recent_popup_menu_clear_item, FALSE);
+      gtk_widget_set_sensitive (impl->recent_popup_menu_show_private_item, FALSE);
+    }
+}
+
+static void
+recent_view_menu_build (GtkRecentChooserDefault *impl)
+{
+  GtkWidget *item;
+  
+  if (impl->recent_popup_menu)
+    {
+      recent_view_menu_ensure_state (impl);
+      
+      return;
+    }
+  
+  impl->recent_popup_menu = gtk_menu_new ();
+  gtk_menu_attach_to_widget (GTK_MENU (impl->recent_popup_menu),
+                            impl->recent_view,
+                            recent_popup_menu_detach_cb);
+  
+  item = gtk_image_menu_item_new_with_mnemonic (_("Copy _Location"));
+  impl->recent_popup_menu_copy_item = item;
+  gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
+                                gtk_image_new_from_stock (GTK_STOCK_COPY, GTK_ICON_SIZE_MENU));
+  g_signal_connect (item, "activate",
+                   G_CALLBACK (copy_activated_cb), impl);
+  gtk_widget_show (item);
+  gtk_menu_shell_append (GTK_MENU_SHELL (impl->recent_popup_menu), item);
+
+  item = gtk_separator_menu_item_new ();
+  gtk_widget_show (item);
+  gtk_menu_shell_append (GTK_MENU_SHELL (impl->recent_popup_menu), item);
+  
+  item = gtk_image_menu_item_new_with_mnemonic (_("_Remove From List"));
+  impl->recent_popup_menu_remove_item = item;
+  gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
+                                gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU));
+  g_signal_connect (item, "activate",
+                   G_CALLBACK (remove_item_activated_cb), impl);
+  gtk_widget_show (item);
+  gtk_menu_shell_append (GTK_MENU_SHELL (impl->recent_popup_menu), item);
+
+  item = gtk_image_menu_item_new_with_mnemonic (_("_Clear List"));
+  impl->recent_popup_menu_clear_item = item;
+  gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
+                                gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
+  g_signal_connect (item, "activate",
+                   G_CALLBACK (remove_all_activated_cb), impl);
+  
+  gtk_widget_show (item);
+  gtk_menu_shell_append (GTK_MENU_SHELL (impl->recent_popup_menu), item);
+  
+  item = gtk_separator_menu_item_new ();
+  gtk_widget_show (item);
+  gtk_menu_shell_append (GTK_MENU_SHELL (impl->recent_popup_menu), item);
+  
+  item = gtk_check_menu_item_new_with_mnemonic (_("Show _Private Resources"));
+  impl->recent_popup_menu_show_private_item = item;
+  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), impl->show_private);
+  g_signal_connect (item, "toggled",
+                   G_CALLBACK (show_private_toggled_cb), impl);
+  gtk_widget_show (item);
+  gtk_menu_shell_append (GTK_MENU_SHELL (impl->recent_popup_menu), item);
+  
+  recent_view_menu_ensure_state (impl);
+}
+
+/* taken from gtkfilechooserdefault.c */
+static void
+popup_position_func (GtkMenu   *menu,
+                     gint      *x,
+                     gint      *y,
+                     gboolean  *push_in,
+                     gpointer  user_data)
+{
+  GtkWidget *widget = GTK_WIDGET (user_data);
+  GdkScreen *screen = gtk_widget_get_screen (widget);
+  GtkRequisition req;
+  gint monitor_num;
+  GdkRectangle monitor;
+
+  if (G_UNLIKELY (!GTK_WIDGET_REALIZED (widget)))
+    return;
+
+  gdk_window_get_origin (widget->window, x, y);
+
+  gtk_widget_size_request (GTK_WIDGET (menu), &req);
+
+  *x += (widget->allocation.width - req.width) / 2;
+  *y += (widget->allocation.height - req.height) / 2;
+
+  monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
+  gtk_menu_set_monitor (menu, monitor_num);
+  gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+  *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
+  *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
+
+  *push_in = FALSE;
+}
+
+
+static void
+recent_view_menu_popup (GtkRecentChooserDefault *impl,
+                       GdkEventButton          *event)
+{
+  recent_view_menu_build (impl);
+  
+  if (event)
+    gtk_menu_popup (GTK_MENU (impl->recent_popup_menu),
+                   NULL, NULL, NULL, NULL,
+                   event->button, event->time);
+  else
+    {
+      gtk_menu_popup (GTK_MENU (impl->recent_popup_menu),
+                     NULL, NULL,
+                     popup_position_func, impl->recent_view,
+                     0, GDK_CURRENT_TIME);
+      gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->recent_popup_menu),
+                                  FALSE);
+    }
+}
+
+static gboolean
+recent_view_popup_menu_cb (GtkWidget *widget,
+                          gpointer   user_data)
+{
+  recent_view_menu_popup (GTK_RECENT_CHOOSER_DEFAULT (user_data), NULL);
+  return TRUE;
+}
+
+static gboolean
+recent_view_button_press_cb (GtkWidget      *widget,
+                            GdkEventButton *event,
+                            gpointer        user_data)
+{
+  GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+  
+  if (event->button == 3)
+    {
+      GtkTreePath *path;
+      gboolean res;
+
+      if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (impl->recent_view)))
+        return FALSE;
+
+      res = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (impl->recent_view),
+                                          event->x, event->y,
+                                          &path,
+                                          NULL, NULL, NULL);
+      if (!res)
+        return FALSE;
+
+      /* select the path before creating the popup menu */
+      gtk_tree_selection_select_path (impl->selection, path);
+      gtk_tree_path_free (path);
+      
+      recent_view_menu_popup (impl, event);
+
+      return TRUE;
+    }
+  
+  return FALSE;
+}
+
+static void
+set_recent_manager (GtkRecentChooserDefault *impl,
+                   GtkRecentManager        *manager)
+{
+  if (impl->manager)
+    {
+      g_signal_handler_disconnect (impl, impl->manager_changed_id);
+      impl->manager_changed_id = 0;
+
+      impl->manager = NULL;
+    }
+  
+  if (manager)
+    impl->manager = manager;
+  else
+    impl->manager = gtk_recent_manager_get_default ();
+  
+  if (impl->manager)
+    impl->manager_changed_id = g_signal_connect (impl->manager, "changed",
+                                                G_CALLBACK (recent_manager_changed_cb),
+                                                impl);
+}
+
+GtkWidget *
+_gtk_recent_chooser_default_new (GtkRecentManager *manager)
+{
+  return g_object_new (GTK_TYPE_RECENT_CHOOSER_DEFAULT,
+                      "recent-manager", manager,
+                      NULL);
+}
+
+#define __GTK_RECENT_CHOOSER_DEFAULT_C__
+#include "gtkaliasdef.c"
diff --git a/gtk/gtkrecentchooserdefault.h b/gtk/gtkrecentchooserdefault.h
new file mode 100644 (file)
index 0000000..852f401
--- /dev/null
@@ -0,0 +1,42 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentchooserdefault.h
+ * Copyright (C) 2006 Emmanuele Bassi
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_RECENT_CHOOSER_DEFAULT_H__
+#define __GTK_RECENT_CHOOSER_DEFAULT_H__
+
+#include <gtk/gtkwidget.h>
+
+G_BEGIN_DECLS
+
+
+#define GTK_TYPE_RECENT_CHOOSER_DEFAULT    (gtk_recent_chooser_default_get_type ())
+#define GTK_RECENT_CHOOSER_DEFAULT(obj)    (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_RECENT_CHOOSER_DEFAULT, GtkRecentChooserDefault))
+#define GTK_IS_RECENT_CHOOSER_DEFAULT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_RECENT_CHOOSER_DEFAULT))
+
+
+typedef struct _GtkRecentChooserDefault GtkRecentChooserDefault;
+
+GType      _gtk_recent_chooser_default_get_type (void) G_GNUC_CONST;
+GtkWidget *_gtk_recent_chooser_default_new      (GtkRecentManager *recent_manager);
+
+
+G_END_DECLS
+
+#endif /* __GTK_RECENT_CHOOSER_DEFAULT_H__ */
diff --git a/gtk/gtkrecentchooserdialog.c b/gtk/gtkrecentchooserdialog.c
new file mode 100644 (file)
index 0000000..a047789
--- /dev/null
@@ -0,0 +1,376 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentchooserdialog.c: Recent files selector dialog
+ * Copyright (C) 2006 Emmanuele Bassi
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include "gtkrecentchooserdialog.h"
+#include "gtkrecentchooserwidget.h"
+#include "gtkrecentchooserutils.h"
+#include "gtkrecentmanager.h"
+#include "gtktypebuiltins.h"
+#include "gtkalias.h"
+
+#include <stdarg.h>
+
+struct _GtkRecentChooserDialogPrivate
+{
+  GtkRecentManager *manager;
+  
+  GtkWidget *chooser;
+};
+
+#define GTK_RECENT_CHOOSER_DIALOG_GET_PRIVATE(obj)     (GTK_RECENT_CHOOSER_DIALOG (obj)->priv)
+
+static void gtk_recent_chooser_dialog_class_init (GtkRecentChooserDialogClass *klass);
+static void gtk_recent_chooser_dialog_init       (GtkRecentChooserDialog      *dialog);
+static void gtk_recent_chooser_dialog_finalize   (GObject                     *object);
+
+static GObject *gtk_recent_chooser_dialog_constructor (GType                  type,
+                                                      guint                  n_construct_properties,
+                                                      GObjectConstructParam *construct_params);
+
+static void gtk_recent_chooser_dialog_set_property (GObject      *object,
+                                                   guint         prop_id,
+                                                   const GValue *value,
+                                                   GParamSpec   *pspec);
+static void gtk_recent_chooser_dialog_get_property (GObject      *object,
+                                                   guint         prop_id,
+                                                   GValue       *value,
+                                                   GParamSpec   *pspec);
+
+static void gtk_recent_chooser_dialog_map       (GtkWidget *widget);
+static void gtk_recent_chooser_dialog_unmap     (GtkWidget *widget);
+static void gtk_recent_chooser_dialog_style_set (GtkWidget *widget,
+                                                GtkStyle  *old_style);
+
+
+G_DEFINE_TYPE_WITH_CODE (GtkRecentChooserDialog,
+                        gtk_recent_chooser_dialog,
+                        GTK_TYPE_DIALOG,
+                        G_IMPLEMENT_INTERFACE (GTK_TYPE_RECENT_CHOOSER,
+                                               _gtk_recent_chooser_delegate_iface_init));
+
+static void
+gtk_recent_chooser_dialog_class_init (GtkRecentChooserDialogClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  
+  gobject_class->set_property = gtk_recent_chooser_dialog_set_property;
+  gobject_class->get_property = gtk_recent_chooser_dialog_get_property;
+  gobject_class->constructor = gtk_recent_chooser_dialog_constructor;
+  gobject_class->finalize = gtk_recent_chooser_dialog_finalize;
+  
+  widget_class->map = gtk_recent_chooser_dialog_map;
+  widget_class->unmap = gtk_recent_chooser_dialog_unmap;
+  widget_class->style_set = gtk_recent_chooser_dialog_style_set;
+  
+  _gtk_recent_chooser_install_properties (gobject_class);
+  
+  g_type_class_add_private (klass, sizeof (GtkRecentChooserDialogPrivate));
+}
+
+static void
+gtk_recent_chooser_dialog_init (GtkRecentChooserDialog *dialog)
+{
+  GtkRecentChooserDialogPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (dialog,
+                                                                    GTK_TYPE_RECENT_CHOOSER_DIALOG,
+                                                                    GtkRecentChooserDialogPrivate);
+  
+  dialog->priv = priv;
+  
+  gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+}
+
+/* we intercept the GtkRecentChooser::item_activated signal and try to
+ * make the dialog emit a valid response signal
+ */
+static void
+gtk_recent_chooser_item_activated_cb (GtkRecentChooser *chooser,
+                                     gpointer          user_data)
+{
+  GtkRecentChooserDialog *dialog;
+  GList *children, *l;
+
+  dialog = GTK_RECENT_CHOOSER_DIALOG (user_data);
+
+  if (gtk_window_activate_default (GTK_WINDOW (dialog)))
+    return;
+  
+  children = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area));
+  
+  for (l = children; l; l = l->next)
+    {
+      GtkWidget *widget;
+      gint response_id;
+      
+      widget = GTK_WIDGET (l->data);
+      response_id = gtk_dialog_get_response_for_widget (GTK_DIALOG (dialog), widget);
+      
+      if (response_id == GTK_RESPONSE_ACCEPT ||
+          response_id == GTK_RESPONSE_OK     ||
+          response_id == GTK_RESPONSE_YES    ||
+          response_id == GTK_RESPONSE_APPLY)
+        {
+          g_list_free (children);
+         
+          gtk_dialog_response (GTK_DIALOG (dialog), response_id);
+
+          return;
+        }
+    }
+  
+  g_list_free (children);
+}
+
+static GObject *
+gtk_recent_chooser_dialog_constructor (GType                  type,
+                                      guint                  n_construct_properties,
+                                      GObjectConstructParam *construct_params)
+{
+  GObject *object;
+  GtkRecentChooserDialogPrivate *priv;
+  
+  object = G_OBJECT_CLASS (gtk_recent_chooser_dialog_parent_class)->constructor (type,
+                                                                                n_construct_properties,
+                                                                                construct_params);
+  priv = GTK_RECENT_CHOOSER_DIALOG_GET_PRIVATE (object);
+  
+  gtk_widget_push_composite_child ();
+  
+  if (priv->manager)
+    priv->chooser = g_object_new (GTK_TYPE_RECENT_CHOOSER_WIDGET,
+                                 "recent-manager", priv->manager,
+                                 NULL);
+  else
+    priv->chooser = g_object_new (GTK_TYPE_RECENT_CHOOSER_WIDGET, NULL);
+  
+  g_signal_connect (priv->chooser, "item-activated",
+                   G_CALLBACK (gtk_recent_chooser_item_activated_cb),
+                   object);
+  
+  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (object)->vbox),
+                      priv->chooser, TRUE, TRUE, 0);
+  gtk_widget_show (priv->chooser);
+  
+  _gtk_recent_chooser_set_delegate (GTK_RECENT_CHOOSER (object),
+                                   GTK_RECENT_CHOOSER (priv->chooser));
+  
+  gtk_widget_pop_composite_child ();
+  
+  return object;
+}
+
+static void
+gtk_recent_chooser_dialog_set_property (GObject      *object,
+                                       guint         prop_id,
+                                       const GValue *value,
+                                       GParamSpec   *pspec)
+{
+  GtkRecentChooserDialogPrivate *priv;
+  
+  priv = GTK_RECENT_CHOOSER_DIALOG_GET_PRIVATE (object);
+  
+  switch (prop_id)
+    {
+    case GTK_RECENT_CHOOSER_PROP_RECENT_MANAGER:
+      priv->manager = g_value_get_object (value);
+      break;
+    default:
+      g_object_set_property (G_OBJECT (priv->chooser), pspec->name, value);
+      break;
+    }
+}
+
+static void
+gtk_recent_chooser_dialog_get_property (GObject      *object,
+                                       guint         prop_id,
+                                       GValue       *value,
+                                       GParamSpec   *pspec)
+{
+  GtkRecentChooserDialogPrivate *priv;
+  
+  priv = GTK_RECENT_CHOOSER_DIALOG_GET_PRIVATE (object);
+  
+  g_object_get_property (G_OBJECT (priv->chooser), pspec->name, value);
+}
+
+static void
+gtk_recent_chooser_dialog_finalize (GObject *object)
+{
+  GtkRecentChooserDialog *dialog = GTK_RECENT_CHOOSER_DIALOG (object);
+  dialog->priv->manager = NULL;
+  
+  G_OBJECT_CLASS (gtk_recent_chooser_dialog_parent_class)->finalize (object);
+}
+
+static void
+gtk_recent_chooser_dialog_map (GtkWidget *widget)
+{
+  GtkRecentChooserDialog *dialog = GTK_RECENT_CHOOSER_DIALOG (widget);
+  GtkRecentChooserDialogPrivate *priv = dialog->priv;
+  
+  if (!GTK_WIDGET_MAPPED (priv->chooser))
+    gtk_widget_map (priv->chooser);
+
+  GTK_WIDGET_CLASS (gtk_recent_chooser_dialog_parent_class)->map (widget);
+}
+
+static void
+gtk_recent_chooser_dialog_unmap (GtkWidget *widget)
+{
+  GtkRecentChooserDialog *dialog = GTK_RECENT_CHOOSER_DIALOG (widget);
+  GtkRecentChooserDialogPrivate *priv = dialog->priv;
+  
+  GTK_WIDGET_CLASS (gtk_recent_chooser_dialog_parent_class)->unmap (widget);
+  
+  gtk_widget_unmap (priv->chooser);
+}
+
+/* taken from gtkfilechooserdialog.c */
+static void
+gtk_recent_chooser_dialog_style_set (GtkWidget *widget,
+                                    GtkStyle  *old_style)
+{
+  GtkDialog *dialog;
+
+  dialog = GTK_DIALOG (widget);
+
+  /* Override the style properties with HIG-compliant spacings.  Ugh.
+   * http://developer.gnome.org/projects/gup/hig/1.0/layout.html#layout-dialogs
+   * http://developer.gnome.org/projects/gup/hig/1.0/windows.html#alert-spacing
+   */
+
+  gtk_container_set_border_width (GTK_CONTAINER (dialog->vbox), 12);
+  gtk_box_set_spacing (GTK_BOX (dialog->vbox), 24);
+
+  gtk_container_set_border_width (GTK_CONTAINER (dialog->action_area), 0);
+  gtk_box_set_spacing (GTK_BOX (dialog->action_area), 6);
+  
+  if (GTK_WIDGET_CLASS (gtk_recent_chooser_dialog_parent_class)->style_set)
+    GTK_WIDGET_CLASS (gtk_recent_chooser_dialog_parent_class)->style_set (widget, old_style);
+}
+
+static GtkWidget *
+gtk_recent_chooser_dialog_new_valist (const gchar      *title,
+                                     GtkWindow        *parent,
+                                     GtkRecentManager *manager,
+                                     const gchar      *first_button_text,
+                                     va_list           varargs)
+{
+  GtkWidget *result;
+  const char *button_text = first_button_text;
+  gint response_id;
+  
+  result = g_object_new (GTK_TYPE_RECENT_CHOOSER_DIALOG,
+                         "title", title,
+                         "recent-manager", manager,
+                         NULL);
+  
+  if (parent)
+    gtk_window_set_transient_for (GTK_WINDOW (result), parent);
+  
+  while (button_text)
+    {
+      response_id = va_arg (varargs, gint);
+      gtk_dialog_add_button (GTK_DIALOG (result), button_text, response_id);
+      button_text = va_arg (varargs, const gchar *);
+    }
+  
+  return result;
+}
+
+/**
+ * gtk_recent_chooser_dialog_new:
+ * @title: Title of the dialog, or %NULL
+ * @parent: Transient parent of the dialog, or %NULL,
+ * @first_button_text: stock ID or text to go in the first button, or %NULL
+ * @Varargs: response ID for the first button, then additional (button, id)
+ *   pairs, ending with %NULL
+ *
+ * Creates a new #GtkRecentChooserDialog.  This function is analogous to
+ * gtk_dialog_new_with_buttons().
+ *
+ * Return value: a new #GtkRecentChooserDialog
+ *
+ * Since: 2.10
+ */
+GtkWidget *
+gtk_recent_chooser_dialog_new (const gchar *title,
+                              GtkWindow   *parent,
+                              const gchar *first_button_text,
+                              ...)
+{
+  GtkWidget *result;
+  va_list varargs;
+  
+  va_start (varargs, first_button_text);
+  result = gtk_recent_chooser_dialog_new_valist (title,
+                                                parent,
+                                                NULL,
+                                                first_button_text,
+                                                varargs);
+  va_end (varargs);
+  
+  return result;
+}
+
+/**
+ * gtk_recent_chooser_dialog_new_for_manager:
+ * @title: Title of the dialog, or %NULL
+ * @parent: Transient parent of the dialog, or %NULL,
+ * @manager: a #GtkRecentManager
+ * @first_button_text: stock ID or text to go in the first button, or %NULL
+ * @Varargs: response ID for the first button, then additional (button, id)
+ *   pairs, ending with %NULL
+ *
+ * Creates a new #GtkRecentChooserDialog with a specified recent manager.
+ *
+ * This is useful if you have implemented your own recent manager, or if you
+ * have a customized instance of a #GtkRecentManager object.
+ *
+ * Return value: a new #GtkRecentChooserDialog
+ *
+ * Since: 2.10
+ */
+GtkWidget *
+gtk_recent_chooser_dialog_new_for_manager (const gchar      *title,
+                                          GtkWindow        *parent,
+                                          GtkRecentManager *manager,
+                                          const gchar      *first_button_text,
+                                          ...)
+{
+  GtkWidget *result;
+  va_list varargs;
+  
+  va_start (varargs, first_button_text);
+  result = gtk_recent_chooser_dialog_new_valist (title,
+                                                parent,
+                                                manager,
+                                                first_button_text,
+                                                varargs);
+  va_end (varargs);
+  
+  return result;
+}
+
+#define __GTK_RECENT_CHOOSER_DIALOG_C__
+#include "gtkaliasdef.c"
diff --git a/gtk/gtkrecentchooserdialog.h b/gtk/gtkrecentchooserdialog.h
new file mode 100644 (file)
index 0000000..22be218
--- /dev/null
@@ -0,0 +1,70 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentchooserdialog.h: Recent files selector dialog
+ * Copyright (C) 2006 Emmanuele Bassi
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_RECENT_CHOOSER_DIALOG_H__
+#define __GTK_RECENT_CHOOSER_DIALOG_H__
+
+#include <gtk/gtkdialog.h>
+#include "gtkrecentchooser.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_RECENT_CHOOSER_DIALOG           (gtk_recent_chooser_dialog_get_type ())
+#define GTK_RECENT_CHOOSER_DIALOG(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_RECENT_CHOOSER_DIALOG, GtkRecentChooserDialog))
+#define GTK_IS_RECENT_CHOOSER_DIALOG(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_RECENT_CHOOSER_DIALOG))
+#define GTK_RECENT_CHOOSER_DIALOG_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_RECENT_CHOOSER_DIALOG, GtkRecentChooserDialogClass))
+#define GTK_IS_RECENT_CHOOSER_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_RECENT_CHOOSER_DIALOG))
+#define GTK_RECENT_CHOOSER_DIALOG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_RECENT_CHOOSER_DIALOG, GtkRecentChooserDialogClass))
+
+typedef struct _GtkRecentChooserDialog        GtkRecentChooserDialog;
+typedef struct _GtkRecentChooserDialogClass   GtkRecentChooserDialogClass;
+
+typedef struct _GtkRecentChooserDialogPrivate GtkRecentChooserDialogPrivate;
+
+
+struct _GtkRecentChooserDialog
+{
+  /*< private >*/
+  GtkDialog parent_instance;
+  
+  GtkRecentChooserDialogPrivate *priv;
+};
+
+struct _GtkRecentChooserDialogClass
+{
+  GtkDialogClass parent_class;
+};
+
+
+GType      gtk_recent_chooser_dialog_get_type        (void) G_GNUC_CONST;
+
+GtkWidget *gtk_recent_chooser_dialog_new             (const gchar      *title,
+                                                     GtkWindow        *parent,
+                                                     const gchar      *first_button_text,
+                                                     ...) G_GNUC_NULL_TERMINATED;
+GtkWidget *gtk_recent_chooser_dialog_new_for_manager (const gchar      *title,
+                                                     GtkWindow        *parent,
+                                                     GtkRecentManager *manager,
+                                                     const gchar      *first_button_text,
+                                                     ...) G_GNUC_NULL_TERMINATED;
+
+G_END_DECLS
+
+#endif /* __GTK_RECENT_CHOOSER_DIALOG_H__ */
diff --git a/gtk/gtkrecentchoosermenu.c b/gtk/gtkrecentchoosermenu.c
new file mode 100644 (file)
index 0000000..90542d9
--- /dev/null
@@ -0,0 +1,1225 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentchoosermenu.c - Recently used items menu widget
+ * Copyright (C) 2005, Emmanuele Bassi
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gdk/gdkscreen.h>
+
+#include "gtkstock.h"
+#include "gtkicontheme.h"
+#include "gtkiconfactory.h"
+#include "gtkintl.h"
+#include "gtksettings.h"
+#include "gtkmenushell.h"
+#include "gtkmenuitem.h"
+#include "gtkimagemenuitem.h"
+#include "gtkseparatormenuitem.h"
+#include "gtkmenu.h"
+#include "gtkimage.h"
+#include "gtkobject.h"
+#include "gtktooltips.h"
+#include "gtktypebuiltins.h"
+#include "gtkalias.h"
+
+#include "gtkrecentmanager.h"
+#include "gtkrecentfilter.h"
+#include "gtkrecentchooser.h"
+#include "gtkrecentchooserutils.h"
+#include "gtkrecentchooserprivate.h"
+#include "gtkrecentchoosermenu.h"
+
+struct _GtkRecentChooserMenuPrivate
+{
+  /* the recent manager object */
+  GtkRecentManager *manager;
+  
+  /* size of the icons of the menu items */  
+  gint icon_size;
+
+  /* RecentChooser properties */
+  gint limit;  
+  guint show_private : 1;
+  guint show_not_found : 1;
+  guint show_tips : 1;
+  guint show_icons : 1;
+  guint local_only : 1;
+  
+  guint show_numbers : 1;
+  
+  GtkRecentSortType sort_type;
+  GtkRecentSortFunc sort_func;
+  gpointer sort_data;
+  GDestroyNotify sort_data_destroy;
+  
+  GSList *filters;
+  GtkRecentFilter *current_filter;
+  guint local_manager : 1;
+  gulong manager_changed_id;
+
+  /* tooltips for our bookmark items*/
+  GtkTooltips *tooltips;
+};
+
+enum {
+  PROP_0,
+
+  PROP_SHOW_NUMBERS
+};
+
+#define FALLBACK_ICON_SIZE     32
+#define FALLBACK_ITEM_LIMIT    10
+
+#define GTK_RECENT_CHOOSER_MENU_GET_PRIVATE(obj)       (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_RECENT_CHOOSER_MENU, GtkRecentChooserMenuPrivate))
+
+static void     gtk_recent_chooser_menu_finalize    (GObject                   *object);
+static GObject *gtk_recent_chooser_menu_constructor (GType                      type,
+                                                    guint                      n_construct_properties,
+                                                    GObjectConstructParam     *construct_params);
+
+static void gtk_recent_chooser_iface_init      (GtkRecentChooserIface     *iface);
+
+static void gtk_recent_chooser_menu_set_property (GObject      *object,
+                                                 guint         prop_id,
+                                                 const GValue *value,
+                                                 GParamSpec   *pspec);
+static void gtk_recent_chooser_menu_get_property (GObject      *object,
+                                                 guint         prop_id,
+                                                 GValue       *value,
+                                                 GParamSpec   *pspec);
+
+static gboolean          gtk_recent_chooser_menu_set_current_uri    (GtkRecentChooser  *chooser,
+                                                                    const gchar       *uri,
+                                                                    GError           **error);
+static gchar *           gtk_recent_chooser_menu_get_current_uri    (GtkRecentChooser  *chooser);
+static gboolean          gtk_recent_chooser_menu_select_uri         (GtkRecentChooser  *chooser,
+                                                                    const gchar       *uri,
+                                                                    GError           **error);
+static void              gtk_recent_chooser_menu_unselect_uri       (GtkRecentChooser  *chooser,
+                                                                    const gchar       *uri);
+static void              gtk_recent_chooser_menu_select_all         (GtkRecentChooser  *chooser);
+static void              gtk_recent_chooser_menu_unselect_all       (GtkRecentChooser  *chooser);
+static GList *           gtk_recent_chooser_menu_get_items          (GtkRecentChooser  *chooser);
+static GtkRecentManager *gtk_recent_chooser_menu_get_recent_manager (GtkRecentChooser  *chooser);
+static void              gtk_recent_chooser_menu_set_sort_func      (GtkRecentChooser  *chooser,
+                                                                    GtkRecentSortFunc  sort_func,
+                                                                    gpointer           sort_data,
+                                                                    GDestroyNotify     data_destroy);
+static void              gtk_recent_chooser_menu_add_filter         (GtkRecentChooser  *chooser,
+                                                                    GtkRecentFilter   *filter);
+static void              gtk_recent_chooser_menu_remove_filter      (GtkRecentChooser  *chooser,
+                                                                    GtkRecentFilter   *filter);
+static GSList *          gtk_recent_chooser_menu_list_filters       (GtkRecentChooser  *chooser);
+static void              gtk_recent_chooser_menu_set_current_filter (GtkRecentChooserMenu *menu,
+                                                                    GtkRecentFilter      *filter);
+
+static void              gtk_recent_chooser_menu_map (GtkWidget *widget);
+static void              gtk_recent_chooser_menu_set_show_tips      (GtkRecentChooserMenu *menu,
+                                                                    gboolean              show_tips);
+
+static void     set_recent_manager (GtkRecentChooserMenu *menu,
+                                   GtkRecentManager     *manager);
+
+static void     chooser_set_sort_type (GtkRecentChooserMenu *menu,
+                                      GtkRecentSortType     sort_type);
+
+static gint     get_icon_size_for_widget (GtkWidget *widget);
+
+static void     item_activate_cb   (GtkWidget        *widget,
+                                   gpointer          user_data);
+static void     manager_changed_cb (GtkRecentManager *manager,
+                                   gpointer          user_data);
+
+G_DEFINE_TYPE_WITH_CODE (GtkRecentChooserMenu,
+                        gtk_recent_chooser_menu,
+                        GTK_TYPE_MENU,
+                        G_IMPLEMENT_INTERFACE (GTK_TYPE_RECENT_CHOOSER,
+                                               gtk_recent_chooser_iface_init));
+
+
+static void
+gtk_recent_chooser_iface_init (GtkRecentChooserIface *iface)
+{
+  iface->set_current_uri = gtk_recent_chooser_menu_set_current_uri;
+  iface->get_current_uri = gtk_recent_chooser_menu_get_current_uri;
+  iface->select_uri = gtk_recent_chooser_menu_select_uri;
+  iface->unselect_uri = gtk_recent_chooser_menu_unselect_uri;
+  iface->select_all = gtk_recent_chooser_menu_select_all;
+  iface->unselect_all = gtk_recent_chooser_menu_unselect_all;
+  iface->get_items = gtk_recent_chooser_menu_get_items;
+  iface->get_recent_manager = gtk_recent_chooser_menu_get_recent_manager;
+  iface->set_sort_func = gtk_recent_chooser_menu_set_sort_func;
+  iface->add_filter = gtk_recent_chooser_menu_add_filter;
+  iface->remove_filter = gtk_recent_chooser_menu_remove_filter;
+  iface->list_filters = gtk_recent_chooser_menu_list_filters;
+}
+
+static void
+gtk_recent_chooser_menu_class_init (GtkRecentChooserMenuClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  
+  gobject_class->constructor = gtk_recent_chooser_menu_constructor;
+  gobject_class->finalize = gtk_recent_chooser_menu_finalize;
+  gobject_class->set_property = gtk_recent_chooser_menu_set_property;
+  gobject_class->get_property = gtk_recent_chooser_menu_get_property;
+
+  widget_class->map = gtk_recent_chooser_menu_map;
+  
+  _gtk_recent_chooser_install_properties (gobject_class);
+
+  /**
+   * GtkRecentChooserMenu:show-numbers
+   *
+   * Whether the first ten items in the menu should be prepended by
+   * a number acting as a unique mnemonic.
+   *
+   * Since: 2.10
+   */
+  g_object_class_install_property (gobject_class,
+                                  PROP_SHOW_NUMBERS,
+                                  g_param_spec_boolean ("show-numbers",
+                                                        P_("Show Numbers"),
+                                                        P_("Whether the items should be displayed with a number"),
+                                                        FALSE,
+                                                        G_PARAM_READWRITE));
+  
+  g_type_class_add_private (klass, sizeof (GtkRecentChooserMenuPrivate));
+}
+
+static void
+gtk_recent_chooser_menu_init (GtkRecentChooserMenu *menu)
+{
+  GtkRecentChooserMenuPrivate *priv;
+  
+  priv = GTK_RECENT_CHOOSER_MENU_GET_PRIVATE (menu);
+  
+  menu->priv = priv;
+  
+  priv->show_icons= TRUE;
+  priv->show_numbers = FALSE;
+  priv->show_tips = FALSE;
+  priv->show_not_found = FALSE;
+  priv->show_private = FALSE;
+  priv->local_only = TRUE;
+  
+  priv->limit = FALLBACK_ITEM_LIMIT;
+
+  priv->sort_type = GTK_RECENT_SORT_NONE;
+  
+  priv->icon_size = FALLBACK_ICON_SIZE;
+  
+  priv->current_filter = NULL;
+    
+  priv->tooltips = gtk_tooltips_new ();
+  g_object_ref_sink (priv->tooltips);
+}
+
+static void
+gtk_recent_chooser_menu_finalize (GObject *object)
+{
+  GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (object);
+  GtkRecentChooserMenuPrivate *priv = menu->priv;
+  
+  g_signal_handler_disconnect (priv->manager, priv->manager_changed_id);
+  priv->manager_changed_id = 0;
+
+  priv->manager = NULL;
+  
+  if (priv->sort_data_destroy)
+    {
+      priv->sort_data_destroy (priv->sort_data);
+      
+      priv->sort_data_destroy = NULL;
+      priv->sort_data = NULL;
+      priv->sort_func = NULL;
+    }
+  
+  if (priv->tooltips)
+    g_object_unref (priv->tooltips);
+  
+  if (priv->current_filter)
+    g_object_unref (priv->current_filter);
+  
+  G_OBJECT_CLASS (gtk_recent_chooser_menu_parent_class)->finalize;
+}
+
+static GObject *
+gtk_recent_chooser_menu_constructor (GType                  type,
+                                    guint                  n_construct_properties,
+                                    GObjectConstructParam *construct_params)
+{
+  GtkRecentChooserMenu *menu;
+  GObject *object;
+  
+  object = G_OBJECT_CLASS (gtk_recent_chooser_menu_parent_class)->constructor (type,
+                                                                              n_construct_properties,
+                                                                              construct_params);
+  menu = GTK_RECENT_CHOOSER_MENU (object);
+  
+  g_assert (menu->priv->manager);
+  
+  return object;
+}
+
+static void
+gtk_recent_chooser_menu_set_property (GObject      *object,
+                                     guint         prop_id,
+                                     const GValue *value,
+                                     GParamSpec   *pspec)
+{
+  GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (object);
+  
+  switch (prop_id)
+    {
+    case PROP_SHOW_NUMBERS:
+      menu->priv->show_numbers = g_value_get_boolean (value);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_RECENT_MANAGER:
+      set_recent_manager (menu, g_value_get_object (value));
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE:
+      menu->priv->show_private = g_value_get_boolean (value);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND:
+      menu->priv->show_not_found = g_value_get_boolean (value);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_TIPS:
+      gtk_recent_chooser_menu_set_show_tips (menu, g_value_get_boolean (value));
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_ICONS:
+      menu->priv->show_icons = g_value_get_boolean (value);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE:
+      g_warning ("%s: RecentChoosers of type `%s' do not support "
+                 "selecting multiple items.",
+                 G_STRFUNC,
+                 G_OBJECT_TYPE_NAME (object));
+      break;
+    case GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY:
+      menu->priv->local_only = g_value_get_boolean (value);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_LIMIT:
+      menu->priv->limit = g_value_get_int (value);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SORT_TYPE:
+      chooser_set_sort_type (menu, g_value_get_enum (value));
+      break;
+    case GTK_RECENT_CHOOSER_PROP_FILTER:
+      gtk_recent_chooser_menu_set_current_filter (menu, g_value_get_object (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_recent_chooser_menu_get_property (GObject    *object,
+                                     guint       prop_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
+{
+  GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (object);
+  
+  switch (prop_id)
+    {
+    case PROP_SHOW_NUMBERS:
+      g_value_set_boolean (value, menu->priv->show_numbers);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_TIPS:
+      g_value_set_boolean (value, menu->priv->show_tips);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_LIMIT:
+      g_value_set_int (value, menu->priv->limit);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY:
+      g_value_set_boolean (value, menu->priv->local_only);
+    case GTK_RECENT_CHOOSER_PROP_SORT_TYPE:
+      g_value_set_enum (value, menu->priv->sort_type);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE:
+      g_value_set_boolean (value, menu->priv->show_private);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND:
+      g_value_set_boolean (value, menu->priv->show_not_found);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SHOW_ICONS:
+      g_value_set_boolean (value, menu->priv->show_icons);
+      break;
+    case GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE:
+      g_warning ("%s: Recent Choosers of type `%s' do not support "
+                 "selecting multiple items.",
+                 G_STRFUNC,
+                 G_OBJECT_TYPE_NAME (object));
+      break;
+    case GTK_RECENT_CHOOSER_PROP_FILTER:
+      g_value_set_object (value, menu->priv->current_filter);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static gboolean
+gtk_recent_chooser_menu_set_current_uri (GtkRecentChooser  *chooser,
+                                        const gchar       *uri,
+                                        GError           **error)
+{
+  GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (chooser);
+  GList *children, *l;
+  GtkWidget *menu_item = NULL;
+  gboolean found = FALSE;
+  
+  children = gtk_container_get_children (GTK_CONTAINER (menu));
+  for (l = children; l != NULL; l = l->next)
+    {
+      GtkRecentInfo *info;
+      
+      menu_item = GTK_WIDGET (l->data);
+      
+      info = g_object_get_data (G_OBJECT (menu_item), "gtk-recent-info");
+      if (!info)
+        continue;
+      
+      if (0 == strcmp (uri, gtk_recent_info_get_uri (info)))
+        found = TRUE;
+    }
+
+  g_list_free (children);
+  
+  if (!found)  
+    {
+      g_set_error (error, GTK_RECENT_CHOOSER_ERROR,
+                  GTK_RECENT_CHOOSER_ERROR_NOT_FOUND,
+                  _("No recently used resource found with URI `%s'"),
+                  uri);
+      return FALSE;
+    }
+  else
+    {
+      gtk_menu_shell_activate_item (GTK_MENU_SHELL (menu), menu_item, TRUE);
+      
+      return TRUE;
+    }
+}
+
+static gchar *
+gtk_recent_chooser_menu_get_current_uri (GtkRecentChooser  *chooser)
+{
+  GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (chooser);
+  GtkWidget *menu_item;
+  GtkRecentInfo *info;
+  
+  menu_item = gtk_menu_get_active (GTK_MENU (menu));
+  if (!menu_item)
+    return NULL;
+  
+  info = g_object_get_data (G_OBJECT (menu_item), "gtk-recent-info");
+  if (!info)
+    return NULL;
+  
+  return g_strdup (gtk_recent_info_get_uri (info));
+}
+
+static gboolean
+gtk_recent_chooser_menu_select_uri (GtkRecentChooser  *chooser,
+                                   const gchar       *uri,
+                                   GError           **error)
+{
+  GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (chooser);
+  GList *children, *l;
+  GtkWidget *menu_item = NULL;
+  gboolean found = FALSE;
+  
+  children = gtk_container_get_children (GTK_CONTAINER (menu));
+  for (l = children; l != NULL; l = l->next)
+    {
+      GtkRecentInfo *info;
+      
+      menu_item = GTK_WIDGET (l->data);
+      
+      info = g_object_get_data (G_OBJECT (menu_item), "gtk-recent-info");
+      if (!info)
+        continue;
+      
+      if (0 == strcmp (uri, gtk_recent_info_get_uri (info)))
+        found = TRUE;
+    }
+
+  g_list_free (children);
+  
+  if (!found)  
+    {
+      g_set_error (error, GTK_RECENT_CHOOSER_ERROR,
+                  GTK_RECENT_CHOOSER_ERROR_NOT_FOUND,
+                  _("No recently used resource found with URI `%s'"),
+                  uri);
+      return FALSE;
+    }
+  else
+    {
+      gtk_menu_shell_select_item (GTK_MENU_SHELL (menu), menu_item);
+
+      return TRUE;
+    }
+}
+
+static void
+gtk_recent_chooser_menu_unselect_uri (GtkRecentChooser *chooser,
+                                      const gchar     *uri)
+{
+  GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (chooser);
+  
+  gtk_menu_shell_deselect (GTK_MENU_SHELL (menu));
+}
+
+static void
+gtk_recent_chooser_menu_select_all (GtkRecentChooser *chooser)
+{
+  g_warning (_("This function is not implemented for "
+               "widgets of class '%s'"),
+             g_type_name (G_OBJECT_TYPE (chooser)));
+}
+
+static void
+gtk_recent_chooser_menu_unselect_all (GtkRecentChooser *chooser)
+{
+  g_warning (_("This function is not implemented for "
+               "widgets of class '%s'."),
+             g_type_name (G_OBJECT_TYPE (chooser)));
+}
+
+static void
+gtk_recent_chooser_menu_set_sort_func (GtkRecentChooser  *chooser,
+                                      GtkRecentSortFunc  sort_func,
+                                      gpointer           sort_data,
+                                      GDestroyNotify     data_destroy)
+{
+  GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (chooser);
+  GtkRecentChooserMenuPrivate *priv = menu->priv;
+  
+  if (priv->sort_data_destroy)
+    {
+      priv->sort_data_destroy (priv->sort_data);
+      
+      priv->sort_func = NULL;
+      priv->sort_data = NULL;
+      priv->sort_data_destroy = NULL;
+    }
+  
+  if (sort_func)
+    {
+      priv->sort_func = sort_func;
+      priv->sort_data = sort_data;
+      priv->sort_data_destroy = data_destroy;
+    }
+}
+
+static gint
+sort_recent_items_mru (GtkRecentInfo *a,
+                      GtkRecentInfo *b,
+                      gpointer       unused)
+{
+  g_assert (a != NULL && b != NULL);
+  
+  return (gtk_recent_info_get_modified (a) < gtk_recent_info_get_modified (b));
+}
+
+static gint
+sort_recent_items_lru (GtkRecentInfo *a,
+                      GtkRecentInfo *b,
+                      gpointer       unused)
+{
+  g_assert (a != NULL && b != NULL);
+  
+  return (gtk_recent_info_get_modified (a) > gtk_recent_info_get_modified (b));
+}
+
+/* our proxy sorting function */
+static gint
+sort_recent_items_proxy (gpointer *a,
+                        gpointer *b,
+                        gpointer  user_data)
+{
+  GtkRecentInfo *info_a = (GtkRecentInfo *) a;
+  GtkRecentInfo *info_b = (GtkRecentInfo *) b;
+  GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (user_data);
+
+  if (menu->priv->sort_func)
+    return (* menu->priv->sort_func) (info_a,
+                                     info_b,
+                                     menu->priv->sort_data);
+  
+  /* fallback */
+  return 0;
+}
+
+static void
+chooser_set_sort_type (GtkRecentChooserMenu *menu,
+                      GtkRecentSortType     sort_type)
+{
+  if (menu->priv->sort_type == sort_type)
+    return;
+
+  menu->priv->sort_type = sort_type;
+}
+
+
+static GList *
+gtk_recent_chooser_menu_get_items (GtkRecentChooser *chooser)
+{
+  GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (chooser);
+  GtkRecentChooserMenuPrivate *priv;
+  gint limit;
+  GtkRecentSortType sort_type;
+  GList *items;
+  GCompareDataFunc compare_func;
+  gint length;
+  
+  priv = menu->priv;
+  
+  if (!priv->manager)
+    return NULL;
+  
+  limit = gtk_recent_chooser_get_limit (chooser);
+  sort_type = gtk_recent_chooser_get_sort_type (chooser);
+
+  switch (sort_type)
+    {
+    case GTK_RECENT_SORT_NONE:
+      compare_func = NULL;
+      break;
+    case GTK_RECENT_SORT_MRU:
+      compare_func = (GCompareDataFunc) sort_recent_items_mru;
+      break;
+    case GTK_RECENT_SORT_LRU:
+      compare_func = (GCompareDataFunc) sort_recent_items_lru;
+      break;
+    case GTK_RECENT_SORT_CUSTOM:
+      compare_func = (GCompareDataFunc) sort_recent_items_proxy;
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+  
+  items = gtk_recent_manager_get_items (priv->manager);
+  if (!items)
+    return NULL;
+  
+  if (compare_func)  
+    items = g_list_sort_with_data (items, compare_func, menu);
+  
+  length = g_list_length (items);
+  if ((limit != -1) && (length > limit))
+    {
+      GList *clamp, *l;
+      
+      clamp = g_list_nth (items, limit - 1);
+
+      l = clamp->next;
+      clamp->next = NULL;      
+      
+      g_list_foreach (l, (GFunc) gtk_recent_info_unref, NULL);
+      g_list_free (l);
+    }
+  
+  return items;
+}
+
+static GtkRecentManager *
+gtk_recent_chooser_menu_get_recent_manager (GtkRecentChooser *chooser)
+{
+  GtkRecentChooserMenuPrivate *priv;
+  priv = GTK_RECENT_CHOOSER_MENU (chooser)->priv;
+  
+  return priv->manager;
+}
+
+static void
+gtk_recent_chooser_menu_add_filter (GtkRecentChooser *chooser,
+                                   GtkRecentFilter  *filter)
+{
+  g_warning (_("This function is not implemented for "
+               "widgets of class '%s'"),
+             g_type_name (G_OBJECT_TYPE (chooser)));
+}
+
+static void
+gtk_recent_chooser_menu_remove_filter (GtkRecentChooser *chooser,
+                                      GtkRecentFilter  *filter)
+{
+  g_warning (_("This function is not implemented for "
+               "widgets of class '%s'"),
+             g_type_name (G_OBJECT_TYPE (chooser)));
+}
+
+static GSList *
+gtk_recent_chooser_menu_list_filters (GtkRecentChooser  *chooser)
+{
+  g_warning (_("This function is not implemented for "
+               "widgets of class '%s'"),
+             g_type_name (G_OBJECT_TYPE (chooser)));
+
+  return NULL;
+}
+
+static void
+gtk_recent_chooser_menu_set_current_filter (GtkRecentChooserMenu *menu,
+                                           GtkRecentFilter      *filter)
+{
+  GtkRecentChooserMenuPrivate *priv;
+
+  priv = menu->priv;
+  
+  if (priv->current_filter)
+    g_object_unref (G_OBJECT (priv->current_filter));
+  
+  priv->current_filter = filter;
+  g_object_ref_sink (priv->current_filter);
+  
+  g_object_notify (G_OBJECT (menu), "filter");
+}
+
+static gboolean
+get_is_recent_filtered (GtkRecentChooserMenu *menu,
+                       GtkRecentInfo        *info)
+{
+  GtkRecentChooserMenuPrivate *priv;
+  GtkRecentFilter *current_filter;
+  GtkRecentFilterInfo filter_info;
+  GtkRecentFilterFlags needed;
+  gboolean retval;
+
+  g_assert (info != NULL);
+
+  priv = menu->priv;
+  
+  if (!priv->current_filter)
+    return FALSE;
+  
+  current_filter = priv->current_filter;
+  needed = gtk_recent_filter_get_needed (current_filter);
+  
+  filter_info.contains = GTK_RECENT_FILTER_URI | GTK_RECENT_FILTER_MIME_TYPE;
+  
+  filter_info.uri = gtk_recent_info_get_uri (info);
+  filter_info.mime_type = gtk_recent_info_get_mime_type (info);
+  
+  if (needed & GTK_RECENT_FILTER_DISPLAY_NAME)
+    {
+      filter_info.display_name = gtk_recent_info_get_display_name (info);
+      filter_info.contains |= GTK_RECENT_FILTER_DISPLAY_NAME;
+    }
+  else
+    filter_info.uri = NULL;
+  
+  if (needed & GTK_RECENT_FILTER_APPLICATION)
+    {
+      filter_info.applications = (const gchar **) gtk_recent_info_get_applications (info, NULL);
+      filter_info.contains |= GTK_RECENT_FILTER_APPLICATION;
+    }
+  else
+    filter_info.applications = NULL;
+
+  if (needed & GTK_RECENT_FILTER_GROUP)
+    {
+      filter_info.groups = (const gchar **) gtk_recent_info_get_groups (info, NULL);
+      filter_info.contains |= GTK_RECENT_FILTER_GROUP;
+    }
+  else
+    filter_info.groups = NULL;
+  
+  if (needed & GTK_RECENT_FILTER_AGE)
+    {
+      filter_info.age = gtk_recent_info_get_age (info);
+      filter_info.contains |= GTK_RECENT_FILTER_AGE;
+    }
+  else
+    filter_info.age = -1;
+  
+  retval = gtk_recent_filter_filter (current_filter, &filter_info);
+  
+  /* this we own */
+  if (filter_info.applications)
+    g_strfreev ((gchar **) filter_info.applications);
+  
+  return !retval;
+}
+
+/* taken from libeel/eel-strings.c */
+static gchar *
+escape_underscores (const gchar *string)
+{
+  gint underscores;
+  const gchar *p;
+  gchar *q;
+  gchar *escaped;
+
+  if (!string)
+    return NULL;
+       
+  underscores = 0;
+  for (p = string; *p != '\0'; p++)
+    underscores += (*p == '_');
+
+  if (underscores == 0)
+    return g_strdup (string);
+
+  escaped = g_new (char, strlen (string) + underscores + 1);
+  for (p = string, q = escaped; *p != '\0'; p++, q++)
+    {
+      /* Add an extra underscore. */
+      if (*p == '_')
+        *q++ = '_';
+      
+      *q = *p;
+    }
+  
+  *q = '\0';
+       
+  return escaped;
+}
+
+static void
+gtk_recent_chooser_menu_add_tip (GtkRecentChooserMenu *menu,
+                                GtkRecentInfo        *info,
+                                GtkWidget            *item)
+{
+  GtkRecentChooserMenuPrivate *priv;
+  gchar *path, *tip_text;
+
+  g_assert (info != NULL);
+  g_assert (item != NULL);
+
+  priv = menu->priv;
+  
+  if (!priv->tooltips)
+    return;
+  
+  path = gtk_recent_info_get_uri_display (info);
+  
+  tip_text = g_strdup_printf (_("Open '%s'"), path);
+  gtk_tooltips_set_tip (priv->tooltips,
+                       item,
+                       tip_text,
+                       NULL);
+
+  g_free (path);  
+  g_free (tip_text);
+}
+
+static GtkWidget *
+gtk_recent_chooser_menu_create_item (GtkRecentChooserMenu *menu,
+                                    GtkRecentInfo        *info,
+                                    gint                  count)
+{
+  GtkRecentChooserMenuPrivate *priv;
+  gchar *label;
+  GtkWidget *item, *image;
+  GdkPixbuf *icon;
+
+  g_assert (info != NULL);
+
+  priv = menu->priv;
+
+  if (priv->show_numbers)
+    {
+      gchar *name, *escaped;
+      
+      name = g_strdup (gtk_recent_info_get_display_name (info));
+      if (!name)
+        name = g_strdup (_("Unknown item"));
+      
+      escaped = escape_underscores (name);
+      
+      /* avoid clashing mnemonics */
+      if (count <= 10)
+        label = g_strdup_printf ("_%d. %s", count, escaped);
+      else
+        label = g_strdup_printf ("%d. %s", count, escaped);
+      
+      item = gtk_image_menu_item_new_with_mnemonic (label);
+      
+      g_free (escaped);
+      g_free (name);
+    }
+  else
+    {
+      label = g_strdup (gtk_recent_info_get_display_name (info));
+      item = gtk_image_menu_item_new_with_label (label);
+    }
+  
+  if (priv->show_icons)
+    {
+      icon = gtk_recent_info_get_icon (info, priv->icon_size);
+        
+      image = gtk_image_new_from_pixbuf (icon);
+      gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+    }
+  
+  if (!gtk_recent_info_exists (info))
+    {
+      gtk_widget_set_sensitive (item, FALSE);
+      
+      goto out;
+    }
+  
+  g_signal_connect (item, "activate",
+                   G_CALLBACK (item_activate_cb),
+                   menu);
+
+out:
+  g_free (label);
+
+  return item;
+}
+
+/* removes the items we own from the menu */
+static void
+gtk_recent_chooser_menu_dispose_items (GtkRecentChooserMenu *menu)
+{
+  GList *children, *l;
+  children = gtk_container_get_children (GTK_CONTAINER (menu));
+  for (l = children; l != NULL; l = l->next)
+    {
+      GtkWidget *menu_item = GTK_WIDGET (l->data);
+      gint mark = 0;
+      
+      /* check for our mark, in order to remove just the items we own */
+      mark = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu_item),
+                                                 "gtk-recent-menu-mark"));
+      if (mark == 1)
+        {
+          GtkRecentInfo *info;
+          
+          /* destroy the attached RecentInfo struct, if found */
+          info = g_object_get_data (G_OBJECT (menu_item), "gtk-recent-info");
+          if (info)
+            g_object_set_data_full (G_OBJECT (menu_item), "gtk-recent-info",
+                                   NULL, NULL);
+          
+          /* and finally remove the item from the menu */
+          gtk_container_remove (GTK_CONTAINER (menu), menu_item);
+        }
+    }
+
+  g_list_free (children);
+}
+
+/* GtkWidget::map method override
+ *
+ * We override this method in order to populate the menu with our
+ * menu items linked to the recently used resources.
+ */
+static void
+gtk_recent_chooser_menu_map (GtkWidget *widget)
+{
+  GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (widget);
+  GtkRecentChooserMenuPrivate *priv = menu->priv;
+  GList *items, *l;
+  gint count;
+  gboolean has_items = FALSE;
+  
+  if (GTK_WIDGET_CLASS (gtk_recent_chooser_menu_parent_class)->map)
+    GTK_WIDGET_CLASS (gtk_recent_chooser_menu_parent_class)->map (widget);
+  
+  priv->icon_size = get_icon_size_for_widget (widget);
+  
+  /* dispose our menu items first */
+  gtk_recent_chooser_menu_dispose_items (menu);
+  items = gtk_recent_chooser_get_items (GTK_RECENT_CHOOSER (menu));
+  
+  count = g_list_length (items);
+  items = g_list_reverse (items);
+
+  for (l = items; l != NULL; l = l->next)
+    {
+      GtkRecentInfo *info = (GtkRecentInfo *) l->data;
+      GtkWidget *item;
+
+      g_assert (info != NULL);
+      
+      /* skip non-local items on request */
+      if (priv->local_only && !gtk_recent_info_is_local (info))
+        continue;
+      
+      /* skip private items on request */
+      if (!priv->show_private && gtk_recent_info_get_private_hint (info))
+        continue;
+      
+      /* skip non-existing items on request */
+      if (!priv->show_not_found && !gtk_recent_info_exists (info))
+        continue;
+
+      /* filter items based on the currently set filter object */
+      if (get_is_recent_filtered (menu, info))
+        continue;
+      item = gtk_recent_chooser_menu_create_item (menu, info, count);
+      if (!item)
+        continue;
+      
+      gtk_recent_chooser_menu_add_tip (menu, info, item);
+      
+      /* FIXME
+       *
+       * We should really place our items taking into account user
+       * defined menu items; this would also remove the need of
+       * reverting the scan order.
+       */
+      gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+      gtk_widget_show (item);
+      
+      /* mark the menu item as one of our own */
+      g_object_set_data (G_OBJECT (item), "gtk-recent-menu-mark",
+                        GINT_TO_POINTER (1));
+      
+      /* attach the RecentInfo object to the menu item, and own a reference
+       * to it, so that it will be destroyed with the menu item when it's
+       * not needed anymore.
+       */
+      g_object_set_data_full (G_OBJECT (item), "gtk-recent-info",
+                             gtk_recent_info_ref (info),
+                             (GDestroyNotify) gtk_recent_info_unref);
+      
+      /* we have at least one item */
+      if (!has_items)
+        has_items = TRUE;
+    }
+  
+  /* now, the RecentInfo objects are bound to the lifetime of the menu */
+  if (items)
+    {
+      g_list_foreach (items,
+                     (GFunc) gtk_recent_info_unref,
+                     NULL);
+      g_list_free (items);
+    }
+
+  /* no recently used resources were found, or they were filtered out, so
+   * we build an item stating that no recently used resources were found
+   * (as night follows the day...).
+   */
+  if (!has_items)
+    {
+      GtkWidget *item;
+      
+      item = gtk_menu_item_new_with_label ("No items found");
+      gtk_widget_set_sensitive (item, FALSE);
+      
+      /* we also mark this item, so that it gets removed when rebuilding
+       * the menu on the next map event
+       */
+      g_object_set_data (G_OBJECT (item), "gtk-recent-menu-mark",
+                        GINT_TO_POINTER (1));
+      
+      gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+      gtk_widget_show (item);
+    }
+}
+
+/* bounce activate signal from the recent menu item widget 
+ * to the recent menu widget
+ */
+static void
+item_activate_cb (GtkWidget *widget,
+                 gpointer   user_data)
+{
+  GtkRecentChooser *chooser = GTK_RECENT_CHOOSER (user_data);
+  
+  _gtk_recent_chooser_item_activated (chooser);
+}
+
+/* we force a redraw if the manager changes when we are showing */
+static void
+manager_changed_cb (GtkRecentManager *manager,
+                   gpointer          user_data)
+{
+  gtk_widget_queue_draw (GTK_WIDGET (user_data));
+}
+
+static void
+set_recent_manager (GtkRecentChooserMenu *menu,
+                   GtkRecentManager     *manager)
+{
+  if (menu->priv->manager)
+    g_signal_handler_disconnect (menu, menu->priv->manager_changed_id);
+  
+  menu->priv->manager = NULL;
+  
+  if (manager)
+    menu->priv->manager = manager;
+  else
+    menu->priv->manager = gtk_recent_manager_get_default ();
+  
+  if (menu->priv->manager)
+    menu->priv->manager_changed_id = g_signal_connect (menu->priv->manager, "changed",
+                                                      G_CALLBACK (manager_changed_cb),
+                                                      menu);
+}
+
+static gint
+get_icon_size_for_widget (GtkWidget *widget)
+{
+  GtkSettings *settings;
+  gint width, height;
+
+  if (gtk_widget_has_screen (widget))
+    settings = gtk_settings_get_for_screen (gtk_widget_get_screen (widget));
+  else
+    settings = gtk_settings_get_default ();
+
+  if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU,
+                                         &width, &height))
+    return MAX (width, height);
+
+  return FALLBACK_ICON_SIZE;
+}
+
+static void
+gtk_recent_chooser_menu_set_show_tips (GtkRecentChooserMenu *menu,
+                                      gboolean              show_tips)
+{
+  if (menu->priv->show_tips == show_tips)
+    return;
+  
+  g_assert (menu->priv->tooltips != NULL);
+  
+  if (show_tips)
+    gtk_tooltips_enable (menu->priv->tooltips);
+  else
+    gtk_tooltips_disable (menu->priv->tooltips);
+  
+  menu->priv->show_tips = show_tips;
+}
+
+/*
+ * Public API
+ */
+
+/**
+ * gtk_recent_chooser_menu_new:
+ *
+ * Creates a new #GtkRecentChooserMenu widget.
+ *
+ * This kind of widget shows the list of recently used resources as
+ * a menu, each item as a menu item.  Each item inside the menu might
+ * have an icon, representing its MIME type, and a number, for mnemonic
+ * access.
+ *
+ * This widget implements the #GtkRecentChooser interface.
+ *
+ * This widget creates its own #GtkRecentManager object.  See the
+ * gtk_recent_chooser_menu_new_for_manager() function to know how to create
+ * a #GtkRecentChooserMenu widget bound to another #GtkRecentManager object.
+ *
+ * Return value: a new #GtkRecentChooserMenu
+ *
+ * Since: 2.10
+ */
+GtkWidget *
+gtk_recent_chooser_menu_new (void)
+{
+  return g_object_new (GTK_TYPE_RECENT_CHOOSER_MENU,
+                      "recent-manager", NULL,
+                      NULL);
+}
+
+/**
+ * gtk_recent_chooser_menu_new_for_manager:
+ * @manager: a #GtkRecentManager
+ *
+ * Creates a new #GtkRecentChooserMenu widget using @manager as
+ * the underlying recently used resources manager.
+ *
+ * This is useful if you have implemented your own recent manager,
+ * or if you have a customized instance of a #GtkRecentManager
+ * object or if you wish to share a common #GtkRecentManager object
+ * among multiple #GtkRecentChooser widgets.
+ *
+ * Return value: a new #GtkRecentChooserMenu, bound to @manager.
+ *
+ * Since: 2.10
+ */
+GtkWidget *
+gtk_recent_chooser_menu_new_for_manager (GtkRecentManager *manager)
+{
+  g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), NULL);
+  
+  return g_object_new (GTK_TYPE_RECENT_CHOOSER_MENU,
+                      "recent-manager", manager,
+                      NULL);
+}
+
+/**
+ * gtk_recent_chooser_menu_get_show_numbers:
+ * @menu: a #GtkRecentChooserMenu
+ *
+ * Returns the value set by gtk_recent_chooser_menu_set_show_numbers().
+ * 
+ * Return value: %TRUE if numbers should be shown.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_chooser_menu_get_show_numbers (GtkRecentChooserMenu *menu)
+{
+  g_return_val_if_fail (GTK_IS_RECENT_CHOOSER_MENU (menu), FALSE);
+
+  return menu->priv->show_numbers;
+}
+
+/**
+ * gtk_recent_chooser_menu_set_show_numbers:
+ * @menu: a #GtkRecentChooserMenu
+ * @show_numbers: whether to show numbers
+ *
+ * Sets whether a number should be added to the items of @menu.  The
+ * numbers are shown to provide a unique character for a mnemonic to
+ * be used inside the menu item's label.  Only the first the items
+ * get a number to avoid clashes.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_menu_set_show_numbers (GtkRecentChooserMenu *menu,
+                                         gboolean              show_numbers)
+{
+  g_return_if_fail (GTK_IS_RECENT_CHOOSER_MENU (menu));
+
+  if (menu->priv->show_numbers == show_numbers)
+    return;
+
+  menu->priv->show_numbers = show_numbers;
+  g_object_notify (G_OBJECT (menu), "show-numbers");
+}
+
+#define __GTK_RECENT_CHOOSER_MENU_C__
+#include "gtkaliasdef.c"
diff --git a/gtk/gtkrecentchoosermenu.h b/gtk/gtkrecentchoosermenu.h
new file mode 100644 (file)
index 0000000..200233f
--- /dev/null
@@ -0,0 +1,70 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentchoosermenu.h - Recently used items menu widget
+ * Copyright (C) 2006, Emmanuele Bassi
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_RECENT_CHOOSER_MENU_H__
+#define __GTK_RECENT_CHOOSER_MENU_H__
+
+#include <gtk/gtkmenu.h>
+#include "gtkrecentchooser.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_RECENT_CHOOSER_MENU           (gtk_recent_chooser_menu_get_type ())
+#define GTK_RECENT_CHOOSER_MENU(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_RECENT_CHOOSER_MENU, GtkRecentChooserMenu))
+#define GTK_IS_RECENT_CHOOSER_MENU(obj)                (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_RECENT_CHOOSER_MENU))
+#define GTK_RECENT_CHOOSER_MENU_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_RECENT_CHOOSER_MENU, GtkRecentChooserMenuClass))
+#define GTK_IS_RECENT_CHOOSER_MENU_CLASS(klass)        (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_RECENT_CHOOSER_MENU))
+#define GTK_RECENT_CHOOSER_MENU_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_RECENT_CHOOSER_MENU, GtkRecentChooserMenuClass))
+
+typedef struct _GtkRecentChooserMenu           GtkRecentChooserMenu;
+typedef struct _GtkRecentChooserMenuClass      GtkRecentChooserMenuClass;
+typedef struct _GtkRecentChooserMenuPrivate    GtkRecentChooserMenuPrivate;
+
+struct _GtkRecentChooserMenu
+{
+  /*< private >*/
+  GtkMenu parent_instance;
+
+  GtkRecentChooserMenuPrivate *priv;
+};
+
+struct _GtkRecentChooserMenuClass
+{
+  GtkMenuClass parent_class;
+  
+  /* padding for future expansion */
+  void (* gtk_recent1) (void);
+  void (* gtk_recent2) (void);
+  void (* gtk_recent3) (void);
+  void (* gtk_recent4) (void);
+};
+
+GType      gtk_recent_chooser_menu_get_type         (void) G_GNUC_CONST;
+
+GtkWidget *gtk_recent_chooser_menu_new              (void);
+GtkWidget *gtk_recent_chooser_menu_new_for_manager  (GtkRecentManager     *manager);
+
+gboolean   gtk_recent_chooser_menu_get_show_numbers (GtkRecentChooserMenu *menu);
+void       gtk_recent_chooser_menu_set_show_numbers (GtkRecentChooserMenu *menu,
+                                                    gboolean              show_numbers);
+
+G_END_DECLS
+
+#endif /* ! __GTK_RECENT_CHOOSER_MENU_H__ */
diff --git a/gtk/gtkrecentchooserprivate.h b/gtk/gtkrecentchooserprivate.h
new file mode 100644 (file)
index 0000000..fcd3c83
--- /dev/null
@@ -0,0 +1,42 @@
+/* gtkrecentprivatechooser.h - Interface definitions for recent selectors UI
+ *
+ * Copyright (C) 2006 Emmanuele Bassi
+ *
+ * All rights reserved
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __GTK_RECENT_CHOOSER_PRIVATE_H__
+#define __GTK_RECENT_CHOOSER_PRIVATE_H__
+
+#include <glib-object.h>
+
+#include "gtkrecentmanager.h"
+#include "gtkrecentchooser.h"
+
+G_BEGIN_DECLS
+
+#define GTK_DEFAULT_RECENT_MANAGER     "gtk-recent-manager-default"
+
+GtkRecentManager *_gtk_recent_chooser_get_recent_manager (GtkRecentChooser *chooser);
+
+void              _gtk_recent_chooser_item_activated     (GtkRecentChooser *chooser);
+void              _gtk_recent_chooser_selection_changed  (GtkRecentChooser *chooser);
+
+G_END_DECLS
+#endif /* ! __GTK_RECENT_CHOOSER_PRIVATE_H__ */
diff --git a/gtk/gtkrecentchooserutils.c b/gtk/gtkrecentchooserutils.c
new file mode 100644 (file)
index 0000000..b7fa81d
--- /dev/null
@@ -0,0 +1,298 @@
+/* gtkrecentchooserutils.h - Private utility functions for implementing a
+ *                           GtkRecentChooser interface
+ *
+ * Copyright (C) 2006 Emmanuele Bassi
+ *
+ * All rights reserved
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Based on gtkfilechooserutils.c:
+ *     Copyright (C) 2003 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include "gtkrecentchooserutils.h"
+
+/* Methods */
+static void      delegate_set_sort_func              (GtkRecentChooser  *chooser,
+                                                     GtkRecentSortFunc  sort_func,
+                                                     gpointer           sort_data,
+                                                     GDestroyNotify     data_destroy);
+static void      delegate_add_filter                 (GtkRecentChooser  *chooser,
+                                                     GtkRecentFilter   *filter);
+static void      delegate_remove_filter              (GtkRecentChooser  *chooser,
+                                                     GtkRecentFilter   *filter);
+static GSList   *delegate_list_filters               (GtkRecentChooser  *chooser);
+static gboolean  delegate_select_uri                 (GtkRecentChooser  *chooser,
+                                                     const gchar       *uri,
+                                                     GError           **error);
+static void      delegate_unselect_uri               (GtkRecentChooser  *chooser,
+                                                     const gchar       *uri);
+static GList    *delegate_get_items                  (GtkRecentChooser  *chooser);
+static GtkRecentManager *delegate_get_recent_manager (GtkRecentChooser  *chooser);
+static void      delegate_select_all                 (GtkRecentChooser  *chooser);
+static void      delegate_unselect_all               (GtkRecentChooser  *chooser);
+static gboolean  delegate_set_current_uri            (GtkRecentChooser  *chooser,
+                                                     const gchar       *uri,
+                                                     GError           **error);
+static gchar *   delegate_get_current_uri            (GtkRecentChooser  *chooser);
+
+/* Signals */
+static void      delegate_notify            (GObject          *object,
+                                            GParamSpec       *pspec,
+                                            gpointer          user_data);
+static void      delegate_selection_changed (GtkRecentChooser *receiver,
+                                            gpointer          user_data);
+static void      delegate_item_activated    (GtkRecentChooser *receiver,
+                                            gpointer          user_data);
+
+/**
+ * _gtk_recent_chooser_install_properties:
+ * @klass: the class structure for a type deriving from #GObject
+ *
+ * Installs the necessary properties for a class implementing
+ * #GtkRecentChooser. A #GtkParamSpecOverride property is installed
+ * for each property, using the values from the #GtkRecentChooserProp
+ * enumeration. The caller must make sure itself that the enumeration
+ * values don't collide with some other property values they
+ * are using.
+ */
+void
+_gtk_recent_chooser_install_properties (GObjectClass *klass)
+{
+  g_object_class_override_property (klass,
+                                   GTK_RECENT_CHOOSER_PROP_RECENT_MANAGER,
+                                   "recent-manager");                                      
+  g_object_class_override_property (klass,
+                                   GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE,
+                                   "show-private");
+  g_object_class_override_property (klass,
+                                   GTK_RECENT_CHOOSER_PROP_SHOW_TIPS,
+                                   "show-tips");
+  g_object_class_override_property (klass,
+                                   GTK_RECENT_CHOOSER_PROP_SHOW_ICONS,
+                                   "show-icons");
+  g_object_class_override_property (klass,
+                                   GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND,
+                                   "show-not-found");
+  g_object_class_override_property (klass,
+                                   GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE,
+                                   "select-multiple");
+  g_object_class_override_property (klass,
+                                   GTK_RECENT_CHOOSER_PROP_LIMIT,
+                                   "limit");
+  g_object_class_override_property (klass,
+                                   GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY,
+                                   "local-only");
+  g_object_class_override_property (klass,
+                                   GTK_RECENT_CHOOSER_PROP_SORT_TYPE,
+                                   "sort-type");
+  g_object_class_override_property (klass,
+                                   GTK_RECENT_CHOOSER_PROP_FILTER,
+                                   "filter");
+}
+
+/**
+ * _gtk_recent_chooser_delegate_iface_init:
+ * @iface: a #GtkRecentChooserIface
+ *
+ * An interface-initialization function for use in cases where
+ * an object is simply delegating the methods, signals of
+ * the #GtkRecentChooser interface to another object.
+ * _gtk_recent_chooser_set_delegate() must be called on each
+ * instance of the object so that the delegate object can
+ * be found.
+ */
+void
+_gtk_recent_chooser_delegate_iface_init (GtkRecentChooserIface *iface)
+{
+  iface->set_current_uri = delegate_set_current_uri;
+  iface->get_current_uri = delegate_get_current_uri;
+  iface->select_uri = delegate_select_uri;
+  iface->unselect_uri = delegate_unselect_uri;
+  iface->select_all = delegate_select_all;
+  iface->unselect_all = delegate_unselect_all;
+  iface->get_items = delegate_get_items;
+  iface->get_recent_manager = delegate_get_recent_manager;
+  iface->set_sort_func = delegate_set_sort_func;
+  iface->add_filter = delegate_add_filter;
+  iface->remove_filter = delegate_remove_filter;
+  iface->list_filters = delegate_list_filters;
+}
+
+/**
+ * _gtk_recent_chooser_set_delegate:
+ * @receiver: a #GObject implementing #GtkRecentChooser
+ * @delegate: another #GObject implementing #GtkRecentChooser
+ *
+ * Establishes that calls on @receiver for #GtkRecentChooser
+ * methods should be delegated to @delegate, and that
+ * #GtkRecentChooser signals emitted on @delegate should be
+ * forwarded to @receiver. Must be used in conjunction with
+ * _gtk_recent_chooser_delegate_iface_init().
+ */
+void
+_gtk_recent_chooser_set_delegate (GtkRecentChooser *receiver,
+                                 GtkRecentChooser *delegate)
+{
+  g_return_if_fail (GTK_IS_RECENT_CHOOSER (receiver));
+  g_return_if_fail (GTK_IS_RECENT_CHOOSER (delegate));
+  
+  g_object_set_data (G_OBJECT (receiver),
+                   "gtk-recent-chooser-delegate", delegate);
+  
+  g_signal_connect (delegate, "notify",
+                   G_CALLBACK (delegate_notify), receiver);
+  g_signal_connect (delegate, "selection-changed",
+                   G_CALLBACK (delegate_selection_changed), receiver);
+  g_signal_connect (delegate, "item-activated",
+                   G_CALLBACK (delegate_item_activated), receiver);
+}
+
+GQuark
+_gtk_recent_chooser_delegate_get_quark (void)
+{
+  static GQuark quark = 0;
+  
+  if (G_UNLIKELY (quark == 0))
+    quark = g_quark_from_static_string ("gtk-recent-chooser-delegate");
+  
+  return quark;
+}
+
+static GtkRecentChooser *
+get_delegate (GtkRecentChooser *receiver)
+{
+  return g_object_get_qdata (G_OBJECT (receiver),
+                            GTK_RECENT_CHOOSER_DELEGATE_QUARK);
+}
+
+static void
+delegate_set_sort_func (GtkRecentChooser  *chooser,
+                       GtkRecentSortFunc  sort_func,
+                       gpointer           sort_data,
+                       GDestroyNotify     data_destroy)
+{
+  gtk_recent_chooser_set_sort_func (get_delegate (chooser),
+                                   sort_func,
+                                   sort_data,
+                                   data_destroy);
+}
+
+static void
+delegate_add_filter (GtkRecentChooser *chooser,
+                    GtkRecentFilter  *filter)
+{
+  gtk_recent_chooser_add_filter (get_delegate (chooser), filter);
+}
+
+static void
+delegate_remove_filter (GtkRecentChooser *chooser,
+                       GtkRecentFilter  *filter)
+{
+  gtk_recent_chooser_remove_filter (get_delegate (chooser), filter);
+}
+
+static GSList *
+delegate_list_filters (GtkRecentChooser *chooser)
+{
+  return gtk_recent_chooser_list_filters (get_delegate (chooser));
+}
+
+static gboolean
+delegate_select_uri (GtkRecentChooser  *chooser,
+                    const gchar       *uri,
+                    GError           **error)
+{
+  return gtk_recent_chooser_select_uri (get_delegate (chooser), uri, error);
+}
+
+static void
+delegate_unselect_uri (GtkRecentChooser *chooser,
+                      const gchar      *uri)
+{
+  return gtk_recent_chooser_unselect_uri (get_delegate (chooser), uri);
+}
+
+static GList *
+delegate_get_items (GtkRecentChooser *chooser)
+{
+  return gtk_recent_chooser_get_items (get_delegate (chooser));
+}
+
+static GtkRecentManager *
+delegate_get_recent_manager (GtkRecentChooser *chooser)
+{
+  return _gtk_recent_chooser_get_recent_manager (get_delegate (chooser));
+}
+
+static void
+delegate_select_all (GtkRecentChooser *chooser)
+{
+  gtk_recent_chooser_select_all (get_delegate (chooser));
+}
+
+static void
+delegate_unselect_all (GtkRecentChooser *chooser)
+{
+  gtk_recent_chooser_unselect_all (get_delegate (chooser));
+}
+
+static gboolean
+delegate_set_current_uri (GtkRecentChooser  *chooser,
+                         const gchar       *uri,
+                         GError           **error)
+{
+  return gtk_recent_chooser_set_current_uri (get_delegate (chooser), uri, error);
+}
+
+static gchar *
+delegate_get_current_uri (GtkRecentChooser *chooser)
+{
+  return gtk_recent_chooser_get_current_uri (get_delegate (chooser));
+}
+
+static void
+delegate_notify (GObject    *object,
+                GParamSpec *pspec,
+                gpointer    user_data)
+{
+  gpointer iface;
+
+  iface = g_type_interface_peek (g_type_class_peek (G_OBJECT_TYPE (object)),
+                                gtk_recent_chooser_get_type ());
+  if (g_object_interface_find_property (iface, pspec->name))
+    g_object_notify (user_data, pspec->name);
+}
+
+static void
+delegate_selection_changed (GtkRecentChooser *receiver,
+                           gpointer          user_data)
+{
+  _gtk_recent_chooser_selection_changed (GTK_RECENT_CHOOSER (user_data));
+}
+
+static void
+delegate_item_activated (GtkRecentChooser *receiver,
+                        gpointer          user_data)
+{
+  _gtk_recent_chooser_item_activated (GTK_RECENT_CHOOSER (user_data));
+}
+
+#define __GTK_RECENT_CHOOSER_UTILS_H__
+#include "gtkaliasdef.c"
diff --git a/gtk/gtkrecentchooserutils.h b/gtk/gtkrecentchooserutils.h
new file mode 100644 (file)
index 0000000..79adca3
--- /dev/null
@@ -0,0 +1,63 @@
+/* gtkrecentchooserutils.h - Private utility functions for implementing a
+ *                           GtkRecentChooser interface
+ *
+ * Copyright (C) 2006 Emmanuele Bassi
+ *
+ * All rights reserved
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Based on gtkfilechooserutils.h:
+ *     Copyright (C) 2003 Red Hat, Inc.
+ */
+#ifndef __GTK_RECENT_CHOOSER_UTILS_H__
+#define __GTK_RECENT_CHOOSER_UTILS_H__
+
+#include "gtkrecentchooserprivate.h"
+
+G_BEGIN_DECLS
+
+
+#define GTK_RECENT_CHOOSER_DELEGATE_QUARK      (_gtk_recent_chooser_delegate_get_quark ())
+
+typedef enum {
+  GTK_RECENT_CHOOSER_PROP_FIRST           = 0x3000,
+  GTK_RECENT_CHOOSER_PROP_RECENT_MANAGER,
+  GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE,
+  GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND,
+  GTK_RECENT_CHOOSER_PROP_SHOW_TIPS,
+  GTK_RECENT_CHOOSER_PROP_SHOW_ICONS,
+  GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE,
+  GTK_RECENT_CHOOSER_PROP_LIMIT,
+  GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY,
+  GTK_RECENT_CHOOSER_PROP_SORT_TYPE,
+  GTK_RECENT_CHOOSER_PROP_FILTER,
+  GTK_RECENT_CHOOSER_PROP_LAST
+} GtkRecentChooserProp;
+
+void   _gtk_recent_chooser_install_properties  (GObjectClass          *klass);
+
+void   _gtk_recent_chooser_delegate_iface_init (GtkRecentChooserIface *iface);
+void   _gtk_recent_chooser_set_delegate        (GtkRecentChooser      *receiver,
+                                               GtkRecentChooser      *delegate);
+
+GQuark _gtk_recent_chooser_delegate_get_quark  (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif /* __GTK_RECENT_CHOOSER_UTILS_H__ */
diff --git a/gtk/gtkrecentchooserwidget.c b/gtk/gtkrecentchooserwidget.c
new file mode 100644 (file)
index 0000000..b3c2115
--- /dev/null
@@ -0,0 +1,194 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentchooserwidget.c: embeddable recently used resources chooser widget
+ * Copyright (C) 2006 Emmanuele Bassi
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include "gtkrecentchooserwidget.h"
+#include "gtkrecentchooserdefault.h"
+#include "gtkrecentchooserutils.h"
+#include "gtktypebuiltins.h"
+#include "gtkalias.h"
+
+struct _GtkRecentChooserWidgetPrivate
+{
+  GtkRecentManager *manager;
+  
+  GtkWidget *chooser;
+};
+
+#define GTK_RECENT_CHOOSER_WIDGET_GET_PRIVATE(obj)     (GTK_RECENT_CHOOSER_WIDGET (obj)->priv)
+
+static GObject *gtk_recent_chooser_widget_constructor  (GType                  type,
+                                                       guint                  n_params,
+                                                       GObjectConstructParam *params);
+static void     gtk_recent_chooser_widget_set_property (GObject               *object,
+                                                       guint                  prop_id,
+                                                       const GValue          *value,
+                                                       GParamSpec            *pspec);
+static void     gtk_recent_chooser_widget_get_property (GObject               *object,
+                                                       guint                  prop_id,
+                                                       GValue                *value,
+                                                       GParamSpec            *pspec);
+static void     gtk_recent_chooser_widget_finalize     (GObject               *object);
+
+
+G_DEFINE_TYPE_WITH_CODE (GtkRecentChooserWidget,
+                        gtk_recent_chooser_widget,
+                        GTK_TYPE_VBOX,
+                        G_IMPLEMENT_INTERFACE (GTK_TYPE_RECENT_CHOOSER,
+                                               _gtk_recent_chooser_delegate_iface_init));
+
+static void
+gtk_recent_chooser_widget_class_init (GtkRecentChooserWidgetClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->constructor = gtk_recent_chooser_widget_constructor;
+  gobject_class->set_property = gtk_recent_chooser_widget_set_property;
+  gobject_class->get_property = gtk_recent_chooser_widget_get_property;
+  gobject_class->finalize = gtk_recent_chooser_widget_finalize;
+
+  _gtk_recent_chooser_install_properties (gobject_class);
+
+  g_type_class_add_private (klass, sizeof (GtkRecentChooserWidgetPrivate));
+}
+
+
+static void
+gtk_recent_chooser_widget_init (GtkRecentChooserWidget *widget)
+{
+  widget->priv = G_TYPE_INSTANCE_GET_PRIVATE (widget, GTK_TYPE_RECENT_CHOOSER_WIDGET,
+                                             GtkRecentChooserWidgetPrivate);
+}
+
+static GObject *
+gtk_recent_chooser_widget_constructor (GType                  type,
+                                      guint                  n_params,
+                                      GObjectConstructParam *params)
+{
+  GObject *object;
+  GtkRecentChooserWidgetPrivate *priv;
+
+  object = G_OBJECT_CLASS (gtk_recent_chooser_widget_parent_class)->constructor (type,
+                                                                                n_params,
+                                                                                params);
+
+  priv = GTK_RECENT_CHOOSER_WIDGET_GET_PRIVATE (object);
+  priv->chooser = _gtk_recent_chooser_default_new (priv->manager);
+  
+  
+  gtk_container_add (GTK_CONTAINER (object), priv->chooser);
+  gtk_widget_show (priv->chooser);
+  _gtk_recent_chooser_set_delegate (GTK_RECENT_CHOOSER (object),
+                                   GTK_RECENT_CHOOSER (priv->chooser));
+
+  return object;
+}
+
+static void
+gtk_recent_chooser_widget_set_property (GObject      *object,
+                                       guint         prop_id,
+                                       const GValue *value,
+                                       GParamSpec   *pspec)
+{
+  GtkRecentChooserWidgetPrivate *priv;
+
+  priv = GTK_RECENT_CHOOSER_WIDGET_GET_PRIVATE (object);
+  
+  switch (prop_id)
+    {
+    case GTK_RECENT_CHOOSER_PROP_RECENT_MANAGER:
+      priv->manager = g_value_get_object (value);
+      break;
+    default:
+      g_object_set_property (G_OBJECT (priv->chooser), pspec->name, value);
+      break;
+    }
+}
+
+static void
+gtk_recent_chooser_widget_get_property (GObject    *object,
+                                       guint       prop_id,
+                                       GValue     *value,
+                                       GParamSpec *pspec)
+{
+  GtkRecentChooserWidgetPrivate *priv;
+
+  priv = GTK_RECENT_CHOOSER_WIDGET_GET_PRIVATE (object);
+
+  g_object_get_property (G_OBJECT (priv->chooser), pspec->name, value);
+}
+
+static void
+gtk_recent_chooser_widget_finalize (GObject *object)
+{
+  GtkRecentChooserWidgetPrivate *priv;
+  
+  priv = GTK_RECENT_CHOOSER_WIDGET_GET_PRIVATE (object);
+  priv->manager = NULL;
+  
+  G_OBJECT_CLASS (gtk_recent_chooser_widget_parent_class)->finalize (object);
+}
+
+/*
+ * Public API
+ */
+
+/**
+ * gtk_recent_chooser_widget_new:
+ * 
+ * Creates a new #GtkRecentChooserWidget object.  This is an embeddable widget
+ * used to access the recently used resources list.
+ *
+ * Return value: a new #GtkRecentChooserWidget
+ *
+ * Since: 2.10
+ */
+GtkWidget *
+gtk_recent_chooser_widget_new (void)
+{
+  return g_object_new (GTK_TYPE_RECENT_CHOOSER_WIDGET, NULL);
+}
+
+/**
+ * gtk_recent_chooser_widget_new_for_manager:
+ * @manager: a #GtkRecentManager
+ *
+ * Creates a new #GtkRecentChooserWidget with a specified recent manager.
+ *
+ * This is useful if you have implemented your own recent manager, or if you
+ * have a customized instance of a #GtkRecentManager object.
+ *
+ * Return value: a new #GtkRecentChooserWidget
+ *
+ * Since: 2.10
+ */
+GtkWidget *
+gtk_recent_chooser_widget_new_for_manager (GtkRecentManager *manager)
+{
+  g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), NULL);
+  
+  return g_object_new (GTK_TYPE_RECENT_CHOOSER_WIDGET,
+                      "recent-manager", manager,
+                      NULL);
+}
+
+#define __GTK_RECENT_CHOOSER_WIDGET_C__
+#include "gtkaliasdef.c"
diff --git a/gtk/gtkrecentchooserwidget.h b/gtk/gtkrecentchooserwidget.h
new file mode 100644 (file)
index 0000000..96aea95
--- /dev/null
@@ -0,0 +1,60 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentchooserwidget.h: embeddable recently used resources chooser widget
+ * Copyright (C) 2006 Emmanuele Bassi
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_RECENT_CHOOSER_WIDGET_H__
+#define __GTK_RECENT_CHOOSER_WIDGET_H__
+
+#include "gtkrecentchooser.h"
+#include <gtk/gtkvbox.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_RECENT_CHOOSER_WIDGET           (gtk_recent_chooser_widget_get_type ())
+#define GTK_RECENT_CHOOSER_WIDGET(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_RECENT_CHOOSER_WIDGET, GtkRecentChooserWidget))
+#define GTK_IS_RECENT_CHOOSER_WIDGET(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_RECENT_CHOOSER_WIDGET))
+#define GTK_RECENT_CHOOSER_WIDGET_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_RECENT_CHOOSER_WIDGET, GtkRecentChooserWidgetClass))
+#define GTK_IS_RECENT_CHOOSER_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_RECENT_CHOOSER_WIDGET))
+#define GTK_RECENT_CHOOSER_WIDGET_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_RECENT_CHOOSER_WIDGET, GtkRecentChooserWidgetClass))
+
+typedef struct _GtkRecentChooserWidget        GtkRecentChooserWidget;
+typedef struct _GtkRecentChooserWidgetClass   GtkRecentChooserWidgetClass;
+
+typedef struct _GtkRecentChooserWidgetPrivate GtkRecentChooserWidgetPrivate;
+
+struct _GtkRecentChooserWidget
+{
+  /*< private >*/
+  GtkVBox parent_instance;
+  
+  GtkRecentChooserWidgetPrivate *priv;
+};
+
+struct _GtkRecentChooserWidgetClass
+{
+  GtkVBoxClass parent_class;
+};
+
+GType      gtk_recent_chooser_widget_get_type        (void) G_GNUC_CONST;
+GtkWidget *gtk_recent_chooser_widget_new             (void);
+GtkWidget *gtk_recent_chooser_widget_new_for_manager (GtkRecentManager *manager);
+
+G_END_DECLS
+
+#endif /* __GTK_RECENT_CHOOSER_WIDGET_H__ */
diff --git a/gtk/gtkrecentfilter.c b/gtk/gtkrecentfilter.c
new file mode 100644 (file)
index 0000000..58476de
--- /dev/null
@@ -0,0 +1,579 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentfilter.h - Filter object for recently used resources
+ * Copyright (C) 2006, Emmanuele Bassi
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include <string.h>
+
+#include "gtkrecentfilter.h"
+#include "gtkobject.h"
+#include "gtkintl.h"
+#include "gtkprivate.h"
+
+#include "gtkalias.h"
+
+#ifdef G_OS_UNIX
+#define XDG_PREFIX _gtk_xdg
+#include "xdgmime/xdgmime.h"
+#endif
+
+typedef struct _GtkRecentFilterClass GtkRecentFilterClass;
+typedef struct _FilterRule FilterRule;
+
+typedef enum {
+  FILTER_RULE_URI,
+  FILTER_RULE_DISPLAY_NAME,
+  FILTER_RULE_MIME_TYPE,
+  FILTER_RULE_PIXBUF_FORMATS,
+  FILTER_RULE_APPLICATION,
+  FILTER_RULE_AGE,
+  FILTER_RULE_GROUP,
+  FILTER_RULE_CUSTOM,
+} FilterRuleType;
+
+struct _GtkRecentFilter
+{
+  GtkObject parent_instance;
+  
+  gchar *name;
+  GSList *rules;
+  
+  GtkRecentFilterFlags needed;
+};
+
+struct _GtkRecentFilterClass
+{
+  GtkObjectClass parent_class;
+};
+
+struct _FilterRule
+{
+  FilterRuleType type;
+  GtkRecentFilterFlags needed;
+  
+  union {
+    gchar *uri;
+    gchar *pattern;
+    gchar *mime_type;
+    GSList *pixbuf_formats;
+    gchar *application;
+    gchar *group;
+    gint age;
+    struct {
+      GtkRecentFilterFunc func;
+      gpointer data;
+      GDestroyNotify data_destroy;
+    } custom;
+  } u;
+};
+
+G_DEFINE_TYPE (GtkRecentFilter, gtk_recent_filter, GTK_TYPE_OBJECT);
+
+
+static void
+filter_rule_free (FilterRule *rule)
+{
+  switch (rule->type)
+    {
+    case FILTER_RULE_MIME_TYPE:
+      g_free (rule->u.mime_type);
+      break;
+    case FILTER_RULE_URI:
+      g_free (rule->u.uri);
+      break;
+    case FILTER_RULE_DISPLAY_NAME:
+      g_free (rule->u.pattern);
+      break;
+    case FILTER_RULE_PIXBUF_FORMATS:
+      g_slist_free (rule->u.pixbuf_formats);
+      break;
+    case FILTER_RULE_AGE:
+      break;
+    case FILTER_RULE_APPLICATION:
+      g_free (rule->u.application);
+      break;
+    case FILTER_RULE_GROUP:
+      g_free (rule->u.group);
+      break;
+    case FILTER_RULE_CUSTOM:
+      if (rule->u.custom.data_destroy)
+        rule->u.custom.data_destroy (rule->u.custom.data);
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+  
+  g_free (rule);
+}
+
+static void
+gtk_recent_filter_finalize (GObject *object)
+{
+  GtkRecentFilter *filter = GTK_RECENT_FILTER (object);
+  
+  if (filter->name)
+    g_free (filter->name);
+  
+  if (filter->rules)
+    {
+      g_slist_foreach (filter->rules,
+                      (GFunc) filter_rule_free,
+                      NULL);
+      g_slist_free (filter->rules);
+    }
+  
+  G_OBJECT_CLASS (gtk_recent_filter_parent_class)->finalize (object);
+}
+
+static void
+gtk_recent_filter_class_init (GtkRecentFilterClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  
+  gobject_class->finalize = gtk_recent_filter_finalize;
+}
+
+static void
+gtk_recent_filter_init (GtkRecentFilter *filter)
+{
+
+}
+
+/*
+ * Public API
+ */
+/**
+ * gtk_recent_filter_new:
+ *
+ * Creates a new #GtkRecentFilter with no rules added to it.
+ * Such filter does not accept any recently used resources, so is not
+ * particularly useful until you add rules with
+ * gtk_recent_filter_add_pattern(), gtk_recent_filter_add_mime_type(),
+ * gtk_recent_filter_add_application(), gtk_recent_filter_add_age().
+ * To create a filter that accepts any recently used resource, use:
+ *
+ * <informalexample><programlisting>
+ * GtkRecentFilter *filter = gtk_recent_filter_new (<!-- -->);
+ * gtk_recent_filter_add_pattern (filter, "*");
+ * </programlisting></informalexample>
+ *
+ * Return value: a new #GtkRecentFilter
+ *
+ * Since: 2.10
+ */
+GtkRecentFilter *
+gtk_recent_filter_new (void)
+{
+  return g_object_new (GTK_TYPE_RECENT_FILTER, NULL);
+}
+
+/**
+ * gtk_recent_filter_set_name:
+ * @filter: a #GtkRecentFilter
+ * @name: then human readable name of @filter
+ *
+ * Sets the human-readable name of the filter; this is the string
+ * that will be displayed in the recently used resources selector
+ * user interface if there is a selectable list of filters.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_filter_set_name (GtkRecentFilter *filter,
+                           const gchar     *name)
+{
+  g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
+  
+  if (filter->name)
+    g_free (filter->name);
+  
+  if (name)
+    filter->name = g_strdup (name);
+}
+
+/**
+ * gtk_recent_filter_get_name:
+ * @filter: a #GtkRecentFilter
+ *
+ * Gets the human-readable name for the filter.
+ * See gtk_recent_filter_set_name().
+ *
+ * Return value: the name of the filter, or %NULL.  The returned string
+ *   is owned by the filter object and should not be freed.
+ *
+ * Since: 2.10
+ */
+G_CONST_RETURN gchar *
+gtk_recent_filter_get_name (GtkRecentFilter *filter)
+{
+  g_return_val_if_fail (GTK_IS_RECENT_FILTER (filter), NULL);
+  
+  return filter->name;
+}
+
+/**
+ * gtk_recent_filter_get_needed:
+ * @filter: a #GtkRecentFilter
+ *
+ * Gets the fields that need to be filled in for the structure
+ * passed to gtk_recent_filter_filter()
+ * 
+ * This function will not typically be used by applications; it
+ * is intended principally for use in the implementation of
+ * #GtkRecentChooser.
+ * 
+ * Return value: bitfield of flags indicating needed fields when
+ *   calling gtk_recent_filter_filter()
+ *
+ * Since: 2.10
+ */
+GtkRecentFilterFlags
+gtk_recent_filter_get_needed (GtkRecentFilter *filter)
+{
+  return filter->needed;
+}
+
+static void
+recent_filter_add_rule (GtkRecentFilter *filter,
+                       FilterRule      *rule)
+{
+  filter->needed |= rule->needed;
+  filter->rules = g_slist_append (filter->rules, rule);
+}
+
+/**
+ * gtk_recent_filter_add_mime_type:
+ * @filter: a #GtkRecentFilter
+ * @mime_type: a MIME type
+ *
+ * Adds a rule that allows resources based on their registered MIME type.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_filter_add_mime_type (GtkRecentFilter *filter,
+                                const gchar     *mime_type)
+{
+  FilterRule *rule;
+  
+  g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
+  g_return_if_fail (mime_type != NULL);
+  
+  rule = g_new0 (FilterRule, 1);
+  rule->type = FILTER_RULE_MIME_TYPE;
+  rule->needed = GTK_RECENT_FILTER_MIME_TYPE;
+  rule->u.mime_type = g_strdup (mime_type);
+  
+  recent_filter_add_rule (filter, rule);
+}
+
+/**
+ * gtk_recent_filter_add_pattern:
+ * @filter: a #GtkRecentFilter
+ * @pattern: a file pattern
+ *
+ * Adds a rule that allows resources based on a pattern matching their
+ * display name.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_filter_add_pattern (GtkRecentFilter *filter,
+                              const gchar     *pattern)
+{
+  FilterRule *rule;
+  
+  g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
+  g_return_if_fail (pattern != NULL);
+  
+  rule = g_new0 (FilterRule, 1);
+  rule->type = FILTER_RULE_DISPLAY_NAME;
+  rule->needed = GTK_RECENT_FILTER_DISPLAY_NAME;
+  rule->u.pattern = g_strdup (pattern);
+  
+  recent_filter_add_rule (filter, rule);
+}
+
+/**
+ * gtk_recent_filter_add_pixbuf_formats:
+ * @filter: a #GtkRecentFilter
+ *
+ * Adds a rule allowing image files in the formats supported
+ * by GdkPixbuf.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_filter_add_pixbuf_formats (GtkRecentFilter *filter)
+{
+  FilterRule *rule;
+
+  g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
+
+  rule = g_new0 (FilterRule, 1);
+  rule->type = FILTER_RULE_PIXBUF_FORMATS;
+  rule->needed = GTK_RECENT_FILTER_MIME_TYPE;
+  rule->u.pixbuf_formats = gdk_pixbuf_get_formats ();
+  
+  recent_filter_add_rule (filter, rule);
+}
+
+/**
+ * gtk_recent_filter_add_application:
+ * @filter: a #GtkRecentFilter
+ * @application: an application name
+ *
+ * Adds a rule that allows resources based on the name of the application
+ * that has registered them.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_filter_add_application (GtkRecentFilter *filter,
+                                  const gchar     *application)
+{
+  FilterRule *rule;
+  
+  g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
+  g_return_if_fail (application != NULL);
+  
+  rule = g_new0 (FilterRule, 1);
+  rule->type = FILTER_RULE_APPLICATION;
+  rule->needed = GTK_RECENT_FILTER_APPLICATION;
+  rule->u.application = g_strdup (application);
+  
+  recent_filter_add_rule (filter, rule);
+}
+
+/**
+ * gtk_recent_filter_add_group:
+ * @filter: a #GtkRecentFilter
+ * @group: a group name
+ *
+ * Adds a rule that allows resources based on the name of the group
+ * to which they belong
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_filter_add_group (GtkRecentFilter *filter,
+                            const gchar     *group)
+{
+  FilterRule *rule;
+  
+  g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
+  g_return_if_fail (group != NULL);
+  
+  rule = g_new0 (FilterRule, 1);
+  rule->type = FILTER_RULE_GROUP;
+  rule->needed = GTK_RECENT_FILTER_GROUP;
+  rule->u.group = g_strdup (group);
+  
+  recent_filter_add_rule (filter, rule);
+}
+
+/**
+ * gtk_recent_filter_add_mime_type:
+ * @filter: a #GtkRecentFilter
+ * @days: number of days
+ *
+ * Adds a rule that allows resources based on their age - that is, the number
+ * of days elapsed since they were last modified.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_filter_add_age (GtkRecentFilter *filter,
+                          gint             days)
+{
+  FilterRule *rule;
+  
+  g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
+  
+  rule = g_new0 (FilterRule, 1);
+  rule->type = FILTER_RULE_AGE;
+  rule->needed = GTK_RECENT_FILTER_AGE;
+  rule->u.age = days;
+  
+  recent_filter_add_rule (filter, rule);
+}
+
+/**
+ * gtk_recent_filter_add_custom:
+ * @filter: a #GtkRecentFilter
+ * @needed: bitfield of flags indicating the information that the custom
+ *          filter function needs.
+ * @func: callback function; if the function returns %TRUE, then
+ *   the file will be displayed.
+ * @data: data to pass to @func
+ * @data_destroy: function to call to free @data when it is no longer needed.
+ * 
+ * Adds a rule to a filter that allows resources based on a custom callback
+ * function. The bitfield @needed which is passed in provides information
+ * about what sorts of information that the filter function needs;
+ * this allows GTK+ to avoid retrieving expensive information when
+ * it isn't needed by the filter.
+ * 
+ * Since: 2.10
+ **/
+void
+gtk_recent_filter_add_custom (GtkRecentFilter      *filter,
+                             GtkRecentFilterFlags  needed,
+                             GtkRecentFilterFunc   func,
+                             gpointer              data,
+                             GDestroyNotify        data_destroy)
+{
+  FilterRule *rule;
+  
+  g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
+  g_return_if_fail (func != NULL);
+
+  rule = g_new0 (FilterRule, 1);
+  rule->type = FILTER_RULE_CUSTOM;
+  rule->needed = needed;
+  rule->u.custom.func = func;
+  rule->u.custom.data = data;
+  rule->u.custom.data_destroy = data_destroy;
+
+  recent_filter_add_rule (filter, rule);
+}
+
+
+/**
+ * gtk_recent_filter_filter:
+ * @filter: a #GtkRecentFilter
+ * @filter_info: a #GtkRecentFilterInfo structure containing information
+ *   about a recently used resource
+ *
+ * Tests whether a file should be displayed according to @filter.
+ * The #GtkRecentFilterInfo structure @filter_info should include
+ * the fields returned from gtk_recent_filter_get_needed().
+ *
+ * This function will not typically be used by applications; it
+ * is intended principally for use in the implementation of
+ * #GtkRecentChooser.
+ * 
+ * Return value: %TRUE if the file should be displayed
+ */
+gboolean
+gtk_recent_filter_filter (GtkRecentFilter           *filter,
+                         const GtkRecentFilterInfo *filter_info)
+{
+  GSList *l;
+  
+  g_return_val_if_fail (GTK_IS_RECENT_FILTER (filter), FALSE);
+  g_return_val_if_fail (filter_info != NULL, FALSE);
+  
+  for (l = filter->rules; l != NULL; l = l->next)
+    {
+      FilterRule *rule = (FilterRule *) l->data;
+
+      if ((filter_info->contains & rule->needed) != rule->needed)
+        continue;
+
+      switch (rule->type)
+        {
+        case FILTER_RULE_MIME_TYPE:
+          if ((filter_info->mime_type != NULL)
+#ifdef G_OS_UNIX
+              && (xdg_mime_mime_type_subclass (filter_info->mime_type, rule->u.mime_type)))
+#else
+             && (strcmp (filter_info->mime_type, rule->u.mime_type) == 0))
+#endif
+            return TRUE;
+          break;
+        case FILTER_RULE_APPLICATION:
+          if (filter_info->applications)
+            {
+              gint i;
+              
+              for (i = 0; filter_info->applications[i] != NULL; i++)
+                {
+                  if (strcmp (filter_info->applications[i], rule->u.application) == 0)
+                    return TRUE;
+                }
+            }
+          break;
+       case FILTER_RULE_GROUP:
+         if (filter_info->groups)
+            {
+             gint i;
+
+             for (i = 0; filter_info->groups[i] != NULL; i++)
+               {
+                 if (strcmp (filter_info->groups[i], rule->u.group) == 0)
+                   return TRUE;
+               }
+           }
+         break;
+       case FILTER_RULE_PIXBUF_FORMATS:
+         {
+            GSList *list;
+           if (!filter_info->mime_type)
+             break;
+
+           for (list = rule->u.pixbuf_formats; list; list = list->next)
+              {
+                gint i;
+               gchar **mime_types;
+
+               mime_types = gdk_pixbuf_format_get_mime_types (list->data);
+
+               for (i = 0; mime_types[i] != NULL; i++)
+                  {
+                    if (strcmp (mime_types[i], filter_info->mime_type) == 0)
+                      {
+                       g_strfreev (mime_types);
+                       return TRUE;
+                     }
+                 }
+
+               g_strfreev (mime_types);
+             }
+           break;
+         }
+        case FILTER_RULE_URI:
+          if ((filter_info->uri != NULL) &&
+              _gtk_fnmatch (rule->u.uri, filter_info->uri, FALSE))
+            return TRUE;
+          break;
+        case FILTER_RULE_DISPLAY_NAME:
+          if ((filter_info->display_name != NULL) &&
+              _gtk_fnmatch (rule->u.pattern, filter_info->display_name, FALSE))
+            return TRUE;
+          break;
+        case FILTER_RULE_AGE:
+          if ((filter_info->age != -1) &&
+              (filter_info->age < rule->u.age))
+            return TRUE;
+          break;
+        case FILTER_RULE_CUSTOM:
+          if (rule->u.custom.func (filter_info, rule->u.custom.data))
+            return TRUE;
+          break;
+        }
+    }
+  
+  return FALSE;
+}
+
+#define __GTK_RECENT_FILTER_C__
+#include "gtkaliasdef.c"
diff --git a/gtk/gtkrecentfilter.h b/gtk/gtkrecentfilter.h
new file mode 100644 (file)
index 0000000..28f6fe7
--- /dev/null
@@ -0,0 +1,90 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentfilter.h - Filter object for recently used resources
+ * Copyright (C) 2006, Emmanuele Bassi
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_RECENT_FILTER_H__
+#define __GTK_RECENT_FILTER_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_RECENT_FILTER         (gtk_recent_filter_get_type ())
+#define GTK_RECENT_FILTER(obj)         (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_RECENT_FILTER, GtkRecentFilter))
+#define GTK_IS_RECENT_FILTER(obj)      (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_RECENT_FILTER))
+
+typedef struct _GtkRecentFilter                GtkRecentFilter;
+typedef struct _GtkRecentFilterInfo    GtkRecentFilterInfo;
+
+typedef enum {
+  GTK_RECENT_FILTER_URI          = 1 << 0,
+  GTK_RECENT_FILTER_DISPLAY_NAME = 1 << 1,
+  GTK_RECENT_FILTER_MIME_TYPE    = 1 << 2,
+  GTK_RECENT_FILTER_APPLICATION  = 1 << 3,
+  GTK_RECENT_FILTER_GROUP        = 1 << 4,
+  GTK_RECENT_FILTER_AGE          = 1 << 5
+} GtkRecentFilterFlags;
+
+typedef gboolean (*GtkRecentFilterFunc) (const GtkRecentFilterInfo *filter_info,
+                                        gpointer                   user_data);
+
+struct _GtkRecentFilterInfo
+{
+  GtkRecentFilterFlags contains;
+
+  const gchar *uri;
+  const gchar *display_name;
+  const gchar *mime_type;
+  const gchar **applications;
+  const gchar **groups;
+  
+  gint age;
+};
+
+GType                 gtk_recent_filter_get_type (void) G_GNUC_CONST;
+
+GtkRecentFilter *     gtk_recent_filter_new      (void);
+void                  gtk_recent_filter_set_name (GtkRecentFilter *filter,
+                                                 const gchar     *name);
+G_CONST_RETURN gchar *gtk_recent_filter_get_name (GtkRecentFilter *filter);
+
+void gtk_recent_filter_add_mime_type      (GtkRecentFilter      *filter,
+                                          const gchar          *mime_type);
+void gtk_recent_filter_add_pattern        (GtkRecentFilter      *filter,
+                                          const gchar          *pattern);
+void gtk_recent_filter_add_pixbuf_formats (GtkRecentFilter      *filter);
+void gtk_recent_filter_add_application    (GtkRecentFilter      *filter,
+                                          const gchar          *application);
+void gtk_recent_filter_add_group          (GtkRecentFilter      *filter,
+                                          const gchar          *group);
+void gtk_recent_filter_add_age            (GtkRecentFilter      *filter,
+                                          gint                  days);
+void gtk_recent_filter_add_custom         (GtkRecentFilter      *filter,
+                                          GtkRecentFilterFlags  needed,
+                                          GtkRecentFilterFunc   func,
+                                          gpointer              data,
+                                          GDestroyNotify        data_destroy);
+
+GtkRecentFilterFlags gtk_recent_filter_get_needed (GtkRecentFilter           *filter);
+gboolean             gtk_recent_filter_filter     (GtkRecentFilter           *filter,
+                                                  const GtkRecentFilterInfo *filter_info);
+
+G_END_DECLS
+
+#endif /* ! __GTK_RECENT_FILTER_H__ */
diff --git a/gtk/gtkrecentmanager.c b/gtk/gtkrecentmanager.c
new file mode 100644 (file)
index 0000000..82e3760
--- /dev/null
@@ -0,0 +1,2424 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentmanager.c: a manager for the recently used resources
+ *
+ * Copyright (C) 2006 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "gtkrecentmanager.h"
+#include "gtkintl.h"
+#include "gtkstock.h"
+#include "gtkicontheme.h"
+#include "gtktypebuiltins.h"
+#include "gtkprivate.h"
+#include "gtkmarshalers.h"
+#include "gtkalias.h"
+
+#ifdef G_OS_UNIX
+#define XDG_PREFIX _gtk_xdg
+#include "xdgmime/xdgmime.h"
+#endif
+
+/* the file where we store the recently used items */
+#define GTK_RECENTLY_USED_FILE ".recently-used.xbel"
+
+/* a poll per second should be enough */
+#define POLL_DELTA     1000
+
+/* return all items by default */
+#define DEFAULT_LIMIT  -1
+
+/* keep in sync with xdgmime */
+#define GTK_RECENT_DEFAULT_MIME        "application/octet-stream"
+
+typedef struct
+{
+  gchar *name;
+  gchar *exec;
+  
+  guint count;
+  
+  time_t stamp;
+} RecentAppInfo;
+
+struct _GtkRecentInfo
+{
+  gchar *uri;
+  
+  gchar *display_name;
+  gchar *description;
+  
+  time_t added;
+  time_t modified;
+  time_t visited;
+  
+  gchar *mime_type;
+  
+  GSList *applications;
+  GHashTable *apps_lookup;
+  
+  GSList *groups;
+  
+  gboolean is_private;
+  
+  GdkPixbuf *icon;
+  
+  gint ref_count;
+};
+
+struct _GtkRecentManagerPrivate
+{
+  gchar *filename;
+
+  guint is_screen_singleton : 1;
+  guint is_dirty : 1;
+  guint write_in_progress : 1;
+  guint read_in_progress : 1;
+  
+  gint limit;
+  gint size;
+
+  GdkScreen *screen;
+  
+  GBookmarkFile *recent_items;
+  
+  time_t last_mtime;
+  guint poll_timeout;
+};
+
+enum
+{
+  PROP_0,
+
+  PROP_FILENAME,  
+  PROP_LIMIT,
+  PROP_SIZE
+};
+
+static void           gtk_recent_manager_finalize     (GObject               *object);
+
+static void           gtk_recent_manager_set_property (GObject               *object,
+                                                      guint                  prop_id,
+                                                      const GValue          *value,
+                                                      GParamSpec            *pspec);
+static void           gtk_recent_manager_get_property (GObject               *object,
+                                                      guint                  prop_id,
+                                                      GValue                *value,
+                                                      GParamSpec            *pspec);
+static void           gtk_recent_manager_changed      (GtkRecentManager      *manager);
+
+static void           gtk_recent_manager_real_changed (GtkRecentManager      *manager);
+static gboolean       gtk_recent_manager_poll_timeout (gpointer               data);
+static void           gtk_recent_manager_set_filename (GtkRecentManager      *manager,
+                                                      const gchar           *filename);
+
+static void           build_recent_items_list         (GtkRecentManager      *manager);
+static void           purge_recent_items_list         (GtkRecentManager      *manager,
+                                                      GError               **error);
+
+static RecentAppInfo *recent_app_info_new             (const gchar           *app_name);
+static void           recent_app_info_free            (RecentAppInfo         *app_info);
+
+static GtkRecentInfo *gtk_recent_info_new             (const gchar           *uri);
+static void           gtk_recent_info_free            (GtkRecentInfo         *recent_info);
+
+static guint signal_changed = 0;
+
+G_DEFINE_TYPE (GtkRecentManager, gtk_recent_manager, G_TYPE_OBJECT);
+
+GQuark
+gtk_recent_manager_error_quark (void)
+{
+  static GQuark quark = 0;
+  if (quark == 0)
+    quark = g_quark_from_static_string ("gtk-recent-manager-error-quark");
+  return quark;
+}
+
+
+static void
+gtk_recent_manager_class_init (GtkRecentManagerClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  
+  gtk_recent_manager_parent_class = g_type_class_peek_parent (klass);
+  
+  gobject_class->set_property = gtk_recent_manager_set_property;
+  gobject_class->get_property = gtk_recent_manager_get_property;
+  gobject_class->finalize = gtk_recent_manager_finalize;
+  
+  /**
+   * GtkRecentManager:filename
+   *
+   * The full path to the file to be used to store and read the recently
+   * used resources list
+   *
+   * Since: 2.10
+   */
+  g_object_class_install_property (gobject_class,
+                                  PROP_FILENAME,
+                                  g_param_spec_string ("filename",
+                                                       P_("Filename"),
+                                                       P_("The full path to the file to be used to store and read the list"),
+                                                       NULL,
+                                                       (G_PARAM_CONSTRUCT_ONLY | G_PARAM_READABLE | G_PARAM_WRITABLE)));
+  /**
+   * GtkRecentManager:limit
+   *
+   * The maximum number of items to be returned by the
+   * gtk_recent_manager_get_items() function.
+   *
+   * Since: 2.10
+   */
+  g_object_class_install_property (gobject_class,
+                                  PROP_LIMIT,
+                                  g_param_spec_int ("limit",
+                                                    P_("Limit"),
+                                                    P_("The maximum number of items to be returned by gtk_recent_manager_get_items()"),
+                                                    -1,
+                                                    G_MAXINT,
+                                                    DEFAULT_LIMIT,
+                                                    G_PARAM_READWRITE));
+  /**
+   * GtkRecentManager:size
+   * 
+   * The size of the recently used resources list.
+   *
+   * Since: 2.10
+   */
+  g_object_class_install_property (gobject_class,
+                                  PROP_SIZE,
+                                  g_param_spec_int ("size",
+                                                    P_("Size"),
+                                                    P_("The size of the recently used resources list"),
+                                                    -1,
+                                                    G_MAXINT,
+                                                    0,
+                                                    G_PARAM_READABLE));
+  
+  /**
+   * GtkRecentManager::changed
+   * @recent_manager: the recent manager
+   *
+   * Emitted when the current recently used resources manager changes its
+   * contents.
+   *
+   * Since: 2.10
+   */
+  signal_changed =
+    g_signal_new (I_("changed"),
+                 G_TYPE_FROM_CLASS (klass),
+                 G_SIGNAL_RUN_FIRST,
+                 G_STRUCT_OFFSET (GtkRecentManagerClass, changed),
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__VOID,
+                 G_TYPE_NONE, 0);
+  
+  klass->changed = gtk_recent_manager_real_changed;
+  
+  g_type_class_add_private (klass, sizeof (GtkRecentManagerPrivate));
+}
+
+static void
+gtk_recent_manager_init (GtkRecentManager *manager)
+{
+  GtkRecentManagerPrivate *priv;
+  
+  priv = g_type_instance_get_private ((GTypeInstance *) manager,
+                                     GTK_TYPE_RECENT_MANAGER);
+  manager->priv = priv;
+  
+  priv->filename = g_build_filename (g_get_home_dir (),
+                                    GTK_RECENTLY_USED_FILE,
+                                    NULL);
+  
+  priv->limit = DEFAULT_LIMIT;
+  priv->size = 0;
+  
+  priv->is_screen_singleton = FALSE;
+  priv->is_dirty = FALSE;
+  priv->write_in_progress = FALSE;
+  priv->read_in_progress = FALSE;
+
+  priv->screen = NULL;
+}
+
+static void
+gtk_recent_manager_set_property (GObject               *object,
+                                guint                  prop_id,
+                                const GValue          *value,
+                                GParamSpec            *pspec)
+{
+  GtkRecentManager *recent_manager = GTK_RECENT_MANAGER (object);
+  switch (prop_id)
+    {
+    case PROP_FILENAME:
+      gtk_recent_manager_set_filename (recent_manager, g_value_get_string (value));
+      break;      
+    case PROP_LIMIT:
+      gtk_recent_manager_set_limit (recent_manager, g_value_get_int (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_recent_manager_get_property (GObject               *object,
+                                guint                  prop_id,
+                                GValue                *value,
+                                GParamSpec            *pspec)
+{
+  GtkRecentManager *recent_manager = GTK_RECENT_MANAGER (object);
+  
+  switch (prop_id)
+    {
+    case PROP_FILENAME:
+      g_value_set_string (value, recent_manager->priv->filename);
+      break;
+    case PROP_LIMIT:
+      g_value_set_int (value, recent_manager->priv->limit);
+      break;
+    case PROP_SIZE:
+      g_value_set_int (value, recent_manager->priv->size);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+} 
+
+static void
+gtk_recent_manager_finalize (GObject *object)
+{
+  GtkRecentManager *manager = GTK_RECENT_MANAGER (object);
+  GtkRecentManagerPrivate *priv = manager->priv;
+
+  /* remove the poll timeout */
+  if (priv->poll_timeout)
+    g_source_remove (priv->poll_timeout);
+  
+  if (priv->filename)
+    g_free (priv->filename);
+  
+  if (priv->recent_items)
+    g_bookmark_file_free (priv->recent_items);
+  
+  /* chain up parent's finalize method */  
+  G_OBJECT_CLASS (gtk_recent_manager_parent_class)->finalize (object);
+}
+
+static void
+gtk_recent_manager_real_changed (GtkRecentManager *manager)
+{
+  GtkRecentManagerPrivate *priv = manager->priv;
+
+  g_object_freeze_notify (G_OBJECT (manager));
+
+  if (priv->is_dirty)
+    {
+      GError *write_error;
+      struct stat stat_buf;
+      
+      /* we are marked as dirty, so we dump the content of our
+       * recently used items list
+       */
+      g_assert (priv->filename != NULL);
+
+      priv->write_in_progress = TRUE;
+
+      /* if no container object has been defined, we create a new
+       * empty container, and dump it
+       */
+      if (!priv->recent_items)
+        {
+          priv->recent_items = g_bookmark_file_new ();
+         priv->size = 0;
+       }
+
+      write_error = NULL;
+      g_bookmark_file_to_file (priv->recent_items,
+                              priv->filename,
+                              &write_error);
+      if (write_error)
+        {
+          g_warning ("Attempting to store changes into `%s', "
+                    "but failed: %s",
+                    priv->filename,
+                    write_error->message);
+         g_error_free (write_error);
+       }
+
+      /* we have sync'ed our list with the storage file, so we
+       * update the file mtime in order to skip the timed check
+       * and spare us from a re-read.
+       */
+      if (g_stat (priv->filename, &stat_buf) < 0)
+       {
+          g_warning ("Unable to stat() the recently used resources file "
+                    "at `%s': %s.",
+                    priv->filename,
+                    g_strerror (errno));
+
+         priv->write_in_progress = FALSE;
+         
+         g_object_thaw_notify (G_OBJECT (manager));
+
+         return;
+       }
+      
+      priv->last_mtime = stat_buf.st_mtime;
+      
+      /* mark us as clean */
+      priv->is_dirty = FALSE;
+    }
+  else
+    {
+      /* we are not marked as dirty, so we have been called
+       * because the recently used resources file has been
+       * changed (and not from us).
+       */
+      build_recent_items_list (manager);
+    }
+
+  g_object_thaw_notify (G_OBJECT (manager));
+}
+
+/* timed poll()-ing of the recently used resources file.
+ * an event-based system would be more efficient.
+ */
+static gboolean
+gtk_recent_manager_poll_timeout (gpointer data)
+{
+  GtkRecentManager *manager = GTK_RECENT_MANAGER (data);
+  GtkRecentManagerPrivate *priv = manager->priv;
+  struct stat stat_buf;
+  int stat_res;
+
+  /* wait for the next timeout if we have a read/write in progress */
+  if (priv->write_in_progress || priv->read_in_progress)
+    return TRUE;
+
+  stat_res = g_stat (priv->filename, &stat_buf);
+  if (stat_res < 0)
+    {
+      /* the file does not exist, yet, so we wait */
+      if (errno == ENOENT)
+        return TRUE;
+      
+      g_warning ("Unable to stat() the recently used resources file "
+                "at `%s': %s.",
+                priv->filename,
+                g_strerror (errno));
+      
+      return TRUE;
+    }
+
+  /* the file didn't change from the last poll(), so we bail out */
+  if (stat_buf.st_mtime == priv->last_mtime)
+    return TRUE;
+
+  /* the file has been changed, hence we emit the "changed" signal */
+  gtk_recent_manager_changed (manager);
+
+  return TRUE;
+}
+
+static void
+gtk_recent_manager_set_filename (GtkRecentManager *manager,
+                                const gchar      *filename)
+{
+  GtkRecentManagerPrivate *priv;
+  
+  g_assert (GTK_IS_RECENT_MANAGER (manager));
+  priv = manager->priv;
+  
+  if (!filename || filename[0] == '\0')
+    return;
+  
+  g_free (manager->priv->filename);
+
+  if (manager->priv->poll_timeout)
+    {
+      g_source_remove (manager->priv->poll_timeout);
+      manager->priv->poll_timeout = 0;
+    }
+
+  build_recent_items_list (manager);
+  
+  priv->filename = g_strdup (filename);
+  priv->poll_timeout = g_timeout_add (POLL_DELTA,
+                                     gtk_recent_manager_poll_timeout,
+                                     manager);
+
+  priv->is_dirty = FALSE;
+}
+
+/* reads the recently used resources file and builds the items list.
+ * we keep the items list inside the parser object, and build the
+ * RecentInfo object only on user's demand to avoid useless replication.
+ */
+static void
+build_recent_items_list (GtkRecentManager *manager)
+{
+  GtkRecentManagerPrivate *priv;
+  struct stat stat_buf;
+  int stat_res;
+  gboolean res;
+  GError *read_error;
+  gint size;
+
+  priv = manager->priv;
+  g_assert (priv->filename != NULL);
+  
+  if (!priv->recent_items)
+    {
+      priv->recent_items = g_bookmark_file_new ();
+      priv->size = 0;
+    }
+
+  stat_res = g_stat (priv->filename, &stat_buf);
+  if (stat_res < 0)
+    {
+      /* the file doesn't exists, so we bail out and wait for the first
+       * write operation
+       */
+
+      if (errno == ENOENT)
+        return;
+      else
+        {
+          g_warning ("Attempting to read the recently used resources file "
+                     "at `%s', but an error occurred: %s. Aborting.",
+                     priv->filename,
+                     g_strerror (errno));
+
+          return;
+        }
+    }
+
+  /* record the last mtime, for later use */
+  priv->last_mtime = stat_buf.st_mtime;
+  
+  priv->read_in_progress = TRUE;
+
+  /* the file exists, and it's valid (we hope); if not, destroy the container
+   * object and hope for a better result when the next "changed" signal is
+   * fired. */
+  read_error = NULL;
+  res = g_bookmark_file_load_from_file (priv->recent_items,
+                                       priv->filename,
+                                       &read_error);
+  if (read_error)
+    {
+      g_warning ("Attempting to read the recently used resources file "
+                "at `%s', but the parser failed: %s.",
+                priv->filename,
+                read_error->message);
+
+      g_bookmark_file_free (priv->recent_items);
+      priv->recent_items = NULL;
+
+      g_error_free (read_error);
+    }
+
+  size = g_bookmark_file_get_size (priv->recent_items);
+  if (priv->size != size)
+    {
+      priv->size = size;
+      
+      g_object_notify (G_OBJECT (manager), "size");
+    }
+
+  priv->read_in_progress = FALSE;
+}
+
+
+/********************
+ * GtkRecentManager *
+ ********************/
+
+
+/**
+ * gtk_recent_manager_new:
+ * 
+ * Creates a new recent manager object.  Recent manager objects are used to
+ * handle the list of recently used resources.  A #GtkRecentManager object
+ * monitors the recently used resources list, and emits the "changed" signal
+ * each time something inside the list changes.
+ *
+ * #GtkRecentManager objects are expansive: be sure to create them only when
+ * needed. You should use the gtk_recent_manager_new_for_screen() or the
+ * gtk_recent_manager_get_default() functions instead.
+ *
+ * Return value: A newly created #GtkRecentManager object.
+ *
+ * Since: 2.10
+ */
+GtkRecentManager *
+gtk_recent_manager_new (void)
+{
+  GtkRecentManager *retval;
+  gchar *filename;
+
+  filename = g_build_filename (g_get_home_dir (),
+                               GTK_RECENTLY_USED_FILE,
+                              NULL);
+  
+  retval = g_object_new (GTK_TYPE_RECENT_MANAGER,
+                         "filename", filename,
+                        NULL);
+  
+  g_free (filename);
+
+  return retval;
+}
+
+/**
+ * gtk_recent_manager_get_default:
+ *
+ * Gets the recent manager for the default screen. See
+ * gtk_recent_manager_get_for_screen().
+ *
+ * Return value: A unique #GtkRecentManager associated with the
+ *   default screen. This recent manager is associated to the
+ *   screen and can be used as long as the screen is open.
+ *   Do no ref or unref it.
+ *
+ * Since: 2.10
+ */
+GtkRecentManager *
+gtk_recent_manager_get_default (void)
+{
+  return gtk_recent_manager_get_for_screen (gdk_screen_get_default ());
+}
+
+/**
+ * gtk_recent_manager_get_for_screen:
+ * @screen: a #GdkScreen
+ *
+ * Gets the recent manager object associated with @screen; if this
+ * function has not previously been called for the given screen,
+ * a new recent manager object will be created and associated with
+ * the screen. Recent manager objects are fairly expensive to create,
+ * so using this function is usually a better choice than calling 
+ * gtk_recent_manager_new() and setting the screen yourself; by using
+ * this function a single recent manager object will be shared between
+ * users.
+ *
+ * Return value: A unique #GtkRecentManager associated with the given
+ *   screen. This recent manager is associated to the with the screen
+ *   and can be used as long as the screen is open. Do not ref or
+ *   unref it.
+ *
+ * Since: 2.10
+ */
+GtkRecentManager *
+gtk_recent_manager_get_for_screen (GdkScreen *screen)
+{
+  GtkRecentManager *manager;
+
+  g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+  g_return_val_if_fail (!screen->closed, NULL);
+
+  manager = g_object_get_data (G_OBJECT (screen), "gtk-recent-manager-default");
+  if (!manager)
+    {
+      GtkRecentManagerPrivate *priv;
+      
+      manager = gtk_recent_manager_new ();
+      gtk_recent_manager_set_screen (manager, screen);
+
+      priv = manager->priv;
+      priv->is_screen_singleton = TRUE;
+
+      g_object_set_data (G_OBJECT (screen), I_("gtk-recent-manager-default"), manager);
+    }
+
+  return manager;
+}
+
+static void
+display_closed (GdkDisplay       *display,
+               gboolean          is_error,
+               GtkRecentManager *manager)
+{
+  GtkRecentManagerPrivate *priv = manager->priv;
+  GdkScreen *screen = priv->screen;
+  gboolean was_screen_singleton = priv->is_screen_singleton;
+
+  if (was_screen_singleton)
+    {
+      g_object_set_data (G_OBJECT (screen), I_("gtk-recent-manager-error-quark"), NULL);
+      priv->is_screen_singleton = FALSE;
+    }
+
+  gtk_recent_manager_set_screen (manager, NULL);
+
+  if (was_screen_singleton)
+    g_object_unref (manager);
+}
+
+static void
+unset_screen (GtkRecentManager *manager)
+{
+  GtkRecentManagerPrivate *priv = manager->priv;
+  GdkDisplay *display;
+
+  if (priv->screen)
+    {
+      display = gdk_screen_get_display (priv->screen);
+
+      g_signal_handlers_disconnect_by_func (display,
+                                           (gpointer) display_closed,
+                                           manager);
+
+      priv->screen = NULL;
+    }
+}
+
+/**
+ * gtk_recent_manager_set_screen:
+ * @manager: a #GtkRecentManager
+ * @screen: a #GdkScreen
+ *
+ * Sets the screen for a recent manager; the screen is used to
+ * track the user's currently configured recently used documents
+ * storage.
+ * 
+ * Since: 2.10
+ */
+void
+gtk_recent_manager_set_screen (GtkRecentManager *manager,
+                              GdkScreen        *screen)
+{
+  GtkRecentManagerPrivate *priv;
+  GdkDisplay *display;
+
+  g_return_if_fail (GTK_IS_RECENT_MANAGER (manager));
+  g_return_if_fail (screen == NULL || GDK_IS_SCREEN (screen));
+
+  priv = manager->priv;
+
+  unset_screen (manager);
+
+  if (screen)
+    {
+      display = gdk_screen_get_display (screen);
+
+      priv->screen = screen;
+
+      g_signal_connect (display, "closed",
+                       G_CALLBACK (display_closed), manager);
+    }
+}
+
+/**
+ * gtk_recent_manager_set_limit:
+ * @recent_manager: a #GtkRecentManager
+ * @limit: the maximum number of items to return, or -1.
+ *
+ * Sets the maximum number of item that the gtk_recent_manager_get_items()
+ * function should return.  If @limit is set to -1, then return all the
+ * items.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_manager_set_limit (GtkRecentManager *recent_manager,
+                             gint              limit)
+{
+  GtkRecentManagerPrivate *priv;
+  
+  g_return_if_fail (GTK_IS_RECENT_MANAGER (recent_manager));
+  
+  priv = recent_manager->priv;
+  priv->limit = limit;
+}
+
+/**
+ * gtk_recent_manager_get_limit:
+ * @recent_manager: a #GtkRecentManager
+ *
+ * Gets the maximum number of items that the gtk_recent_manager_get_items()
+ * function should return.
+ *
+ * Return value: the number of items to return, or -1 for every item.
+ *
+ * Since: 2.10
+ */
+gint
+gtk_recent_manager_get_limit (GtkRecentManager *recent_manager)
+{
+  GtkRecentManagerPrivate *priv;
+  
+  g_return_val_if_fail (GTK_IS_RECENT_MANAGER (recent_manager), DEFAULT_LIMIT);
+  
+  priv = recent_manager->priv;
+  return priv->limit;
+}
+
+/**
+ * gtk_recent_manager_add_item:
+ * @recent_manager: a #GtkRecentManager
+ * @uri: a valid URI
+ * @error: return location for a #GError, or %NULL
+ *
+ * Adds a new resource, pointed by @uri, into the recently used
+ * resources list.
+ *
+ * This function automatically retrieving some of the needed
+ * metadata and setting other metadata to common default values; it
+ * then feeds the data to gtk_recent_manager_add_full().
+ *
+ * See gtk_recent_manager_add_full() if you want to explicitely
+ * define the metadata for the resource pointed by @uri.
+ *
+ * Return value: %TRUE if the new item was successfully added
+ *   to the recently used resources list
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_manager_add_item (GtkRecentManager  *recent_manager,
+                            const gchar       *uri,
+                            GError           **error)
+{
+  GtkRecentData *recent_data;
+  GError *add_error;
+  gboolean retval;
+  
+  g_return_val_if_fail (GTK_IS_RECENT_MANAGER (recent_manager), FALSE);
+  g_return_val_if_fail (uri != NULL, FALSE);
+
+  recent_data = g_slice_new (GtkRecentData);
+  
+  recent_data->display_name = NULL;
+  recent_data->description = NULL;
+  
+#ifdef G_OS_UNIX
+  if (g_str_has_prefix (uri, "file://"))
+    {
+      gchar *filename;
+      const gchar *mime_type;
+      
+      filename = g_filename_from_uri (uri, NULL, NULL);
+      mime_type = xdg_mime_get_mime_type_for_file (filename, NULL);
+      if (!mime_type)
+        recent_data->mime_type = g_strdup (GTK_RECENT_DEFAULT_MIME);
+      else
+        recent_data->mime_type = g_strdup (mime_type);
+      
+      g_free (filename);
+    }
+  else
+#endif
+    recent_data->mime_type = g_strdup (GTK_RECENT_DEFAULT_MIME);
+  
+  recent_data->app_name = g_strdup (g_get_application_name ());
+  recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%u", NULL);
+  
+  recent_data->groups = NULL;
+  
+  recent_data->is_private = FALSE;
+  
+  add_error = NULL;
+  retval = gtk_recent_manager_add_full (recent_manager, uri, recent_data, &add_error);
+  
+  g_free (recent_data->mime_type);
+  g_free (recent_data->app_name);
+  g_free (recent_data->app_exec);
+
+  g_slice_free (GtkRecentData, recent_data);
+  
+  if (!retval)
+    {
+      g_propagate_error (error, add_error);
+      
+      return FALSE;
+    }
+  
+  return retval;
+}
+
+/**
+ * gtk_recent_manager_add_full:
+ * @recent_manager: a #GtkRecentManager
+ * @uri: a valid URI
+ * @recent_data: metadata of the resource
+ * @error: return location for a #GError, or %NULL
+ *
+ * Adds a new resource, pointed by @uri, into the recently used
+ * resources list, using the metadata specified inside the #GtkRecentData
+ * structure passed in @recent_data.
+ *
+ * The passed URI will be used to identify this resource inside the
+ * list.
+ *
+ * In order to register the new recently used resource, metadata about
+ * the resource must be passed as well as the URI; the metadata is
+ * stored in a #GtkRecentData structure, which must contain the MIME
+ * type of the resource pointed by the URI; the name of the application
+ * that is registering the item, and a command line to be used when
+ * launching the item.
+ *
+ * Optionally, a #GtkRecentData structure might contain a UTF-8 string
+ * to be used when viewing the item instead of the last component of the
+ * URI; a short description of the item; whether the item should be
+ * considered private - that is, should be displayed only by the
+ * applications that have registered it.
+ *
+ * Return value: %TRUE if the new item was successfully added to the
+ * recently used resources list, %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_manager_add_full (GtkRecentManager     *recent_manager,
+                            const gchar          *uri,
+                            const GtkRecentData  *data,
+                            GError              **error)
+{
+  GtkRecentManagerPrivate *priv;
+  
+  g_return_val_if_fail (GTK_IS_RECENT_MANAGER (recent_manager), FALSE);
+  g_return_val_if_fail (uri != NULL, FALSE);
+  g_return_val_if_fail (data != NULL, FALSE);
+
+  /* sanity checks */
+  if ((data->display_name) &&
+      (!g_utf8_validate (data->display_name, -1, NULL)))
+    {
+      g_set_error  (error, GTK_RECENT_MANAGER_ERROR,
+                   GTK_RECENT_MANAGER_ERROR_INVALID_ENCODING,
+                   _("The display name of the recently used resource "
+                     "must be a valid UTF-8 encoded string."));
+      return FALSE;
+    }
+  
+  if ((data->description) &&
+      (!g_utf8_validate (data->description, -1, NULL)))
+    {
+      g_set_error (error, GTK_RECENT_MANAGER_ERROR,
+                  GTK_RECENT_MANAGER_ERROR_INVALID_ENCODING,
+                  _("The description of the recently used resource "
+                    "must by a valid UTF-8 encoded string."));
+      return FALSE;
+    }
+
+  if (!data->mime_type)
+    {
+      g_set_error (error, GTK_RECENT_MANAGER_ERROR,
+                   GTK_RECENT_MANAGER_ERROR_INVALID_MIME,
+                  _("You must specify the MIME type of the "
+                    "resource pointed by `%s'"),
+                  uri);
+      return FALSE;
+    }
+  
+  if (!data->app_name)
+    {
+      g_set_error (error, GTK_RECENT_MANAGER_ERROR,
+                  GTK_RECENT_MANAGER_ERROR_NOT_REGISTERED,
+                  _("You must specify the name of the application "
+                    "that is registering the recently used resource "
+                    "pointed by `%s'"),
+                  uri);
+      return FALSE;
+    }
+  
+  if (!data->app_exec)
+    {
+      g_set_error (error, GTK_RECENT_MANAGER_ERROR,
+                   GTK_RECENT_MANAGER_ERROR_BAD_EXEC_STRING,
+                  _("You must specify a command line to "
+                    "be used when launching the resource "
+                    "pointed by `%s'"),
+                  uri);
+      return FALSE;
+    }
+  
+  priv = recent_manager->priv;
+
+  if (!priv->recent_items)
+    {
+      priv->recent_items = g_bookmark_file_new ();
+      priv->size = 0;
+    }
+
+  if (data->display_name)  
+    g_bookmark_file_set_title (priv->recent_items, uri, data->display_name);
+  
+  if (data->description)
+    g_bookmark_file_set_description (priv->recent_items, uri, data->description);
+
+  g_bookmark_file_set_mime_type (priv->recent_items, uri, data->mime_type);
+  
+  if (data->groups && data->groups[0] != '\0')
+    {
+      gint j;
+      
+      for (j = 0; (data->groups)[j] != NULL; j++)
+        g_bookmark_file_add_group (priv->recent_items, uri, (data->groups)[j]);
+    }
+  
+  /* register the application; this will take care of updating the
+   * registration count and time in case the application has
+   * already registered the same document inside the list
+   */
+  g_bookmark_file_add_application (priv->recent_items, uri,
+                                  data->app_name,
+                                  data->app_exec);
+  
+  g_bookmark_file_set_is_private (priv->recent_items, uri,
+                                 data->is_private);
+  
+  /* mark us as dirty, so that when emitting the "changed" signal we
+   * will dump our changes
+   */
+  priv->is_dirty = TRUE;
+  
+  gtk_recent_manager_changed (recent_manager);
+  
+  return TRUE;
+}
+
+/**
+ * gtk_recent_manager_remove_item:
+ * @recent_manager: a #GtkRecentManager
+ * @uri: the URI of the item you wish to remove
+ * @error: return location for a #GError, or %NULL
+ *
+ * Removes a resource pointed by @uri from the recently used resources
+ * list handled by a recent manager.
+ *
+ * Return value: %TRUE if the item pointed by @uri has been successfully
+ *   removed by the recently used resources list, and %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_manager_remove_item (GtkRecentManager  *recent_manager,
+                               const gchar       *uri,
+                               GError           **error)
+{
+  GtkRecentManagerPrivate *priv;
+  GError *remove_error = NULL;
+
+  g_return_val_if_fail (GTK_IS_RECENT_MANAGER (recent_manager), FALSE);
+  g_return_val_if_fail (uri != NULL, FALSE);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+  
+  priv = recent_manager->priv;
+  
+  if (!priv->recent_items)
+    {
+      priv->recent_items = g_bookmark_file_new ();
+      priv->size = 0;
+
+      g_set_error (error, GTK_RECENT_MANAGER_ERROR,
+                  GTK_RECENT_MANAGER_ERROR_NOT_FOUND,
+                  _("Unable to find an item with URI '%s'"),
+                  uri);
+
+      return FALSE;
+    }
+
+  g_bookmark_file_remove_item (priv->recent_items, uri, &remove_error);
+  if (remove_error)
+    {
+      g_propagate_error (error, remove_error);
+      
+      return FALSE;
+    }
+
+  priv->is_dirty = TRUE;
+
+  gtk_recent_manager_changed (recent_manager);
+  
+  return TRUE;
+}
+
+/**
+ * gtk_recent_manager_has_item:
+ * @recent_manager: a #GtkRecentManager
+ * @uri: a URI
+ *
+ * Checks whether there is a recently used resource registered
+ * with @uri inside the recent manager.
+ *
+ * Return value: %TRUE if the resource was found, %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_manager_has_item (GtkRecentManager *recent_manager,
+                            const gchar      *uri)
+{
+  GtkRecentManagerPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_RECENT_MANAGER (recent_manager), FALSE);
+  g_return_val_if_fail (uri != NULL, FALSE);
+
+  priv = recent_manager->priv;
+  g_return_val_if_fail (priv->recent_items != NULL, FALSE);
+
+  return g_bookmark_file_has_item (priv->recent_items, uri);
+}
+
+static gboolean
+build_recent_info (GBookmarkFile  *bookmarks,
+                  GtkRecentInfo  *info)
+{
+  gchar **apps, **groups;
+  gsize apps_len, groups_len, i;
+
+  g_assert (bookmarks != NULL);
+  g_assert (info != NULL);
+  
+  info->display_name = g_bookmark_file_get_title (bookmarks, info->uri, NULL);
+  info->description = g_bookmark_file_get_description (bookmarks, info->uri, NULL);
+  info->mime_type = g_bookmark_file_get_mime_type (bookmarks, info->uri, NULL);
+    
+  info->is_private = g_bookmark_file_get_is_private (bookmarks, info->uri, NULL);
+  
+  info->added = g_bookmark_file_get_added (bookmarks, info->uri, NULL);
+  info->modified = g_bookmark_file_get_modified (bookmarks, info->uri, NULL);
+  info->visited = g_bookmark_file_get_visited (bookmarks, info->uri, NULL);
+  
+  groups = g_bookmark_file_get_groups (bookmarks, info->uri, &groups_len, NULL);
+  for (i = 0; i < groups_len; i++)
+    {
+      gchar *group_name = g_strdup (groups[i]);
+      
+      info->groups = g_slist_append (info->groups, group_name);
+    }
+
+  g_strfreev (groups);
+  
+  apps = g_bookmark_file_get_applications (bookmarks, info->uri, &apps_len, NULL);
+  for (i = 0; i < apps_len; i++)
+    {
+      gchar *app_name, *app_exec;
+      guint count;
+      time_t stamp;
+      RecentAppInfo *app_info;
+      gboolean res;
+      
+      app_name = apps[i];
+      
+      res = g_bookmark_file_get_app_info (bookmarks, info->uri, app_name,
+                                         &app_exec,
+                                         &count,
+                                         &stamp,
+                                         NULL);
+      if (!res)
+        continue;
+      
+      app_info = recent_app_info_new (app_name);
+      app_info->exec = app_exec;
+      app_info->count = count;
+      app_info->stamp = stamp;
+      
+      info->applications = g_slist_append (info->applications,
+                                          app_info);
+      g_hash_table_replace (info->apps_lookup, app_info->name, app_info);
+    }
+  
+  g_strfreev (apps);
+  
+  return TRUE; 
+}
+
+/**
+ * gtk_recent_manager_lookup_item:
+ * @recent_manager: a #GtkRecentManager
+ * @uri: a URI
+ * @error: a return location for a #GError, or %NULL
+ *
+ * Searches for a URI inside the recently used resources list, and
+ * returns a structure containing informations about the resource
+ * like its MIME type, or its display name.
+ *
+ * Return value: a #GtkRecentInfo structure containing information
+ *   about the resource pointed by @uri, or %NULL if the URI was
+ *   not registered in the recently used resources list.  Free with
+ *   gtk_recent_info_unref().
+ **/
+GtkRecentInfo *
+gtk_recent_manager_lookup_item (GtkRecentManager  *recent_manager,
+                               const gchar       *uri,
+                               GError           **error)
+{
+  GtkRecentManagerPrivate *priv;
+  GtkRecentInfo *info = NULL;
+  gboolean res;
+  
+  g_return_val_if_fail (GTK_IS_RECENT_MANAGER (recent_manager), NULL);
+  g_return_val_if_fail (uri != NULL, NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+  
+  priv = recent_manager->priv;
+  if (!priv->recent_items)
+    {
+      priv->recent_items = g_bookmark_file_new ();
+      priv->size = 0;
+
+      g_set_error (error, GTK_RECENT_MANAGER_ERROR,
+                  GTK_RECENT_MANAGER_ERROR_NOT_FOUND,
+                  _("Unable to find an item with URI '%s'"),
+                  uri);
+
+      return NULL;
+    }
+  
+  if (!g_bookmark_file_has_item (priv->recent_items, uri))
+    {
+      g_set_error (error, GTK_RECENT_MANAGER_ERROR,
+                  GTK_RECENT_MANAGER_ERROR_NOT_FOUND,
+                  _("Unable to find an item with URI '%s'"),
+                  uri);
+      return NULL;
+    }
+  
+  info = gtk_recent_info_new (uri);
+  g_return_val_if_fail (info != NULL, NULL);
+  
+  /* fill the RecentInfo structure with the data retrieved by our
+   * parser object from the storage file 
+   */
+  res = build_recent_info (priv->recent_items, info);
+  if (!res)
+    {
+      gtk_recent_info_free (info);
+      
+      return NULL;
+    }
+  return gtk_recent_info_ref (info);
+}
+
+/**
+ * gtk_recent_manager_move_item:
+ * @manager: a #GtkRecentManager
+ * @uri: the URI of a recently used resource
+ * @new_uri: the new URI of the recently used resource, or %NULL to
+ *    remove the item pointed by @uri in the list
+ * @error: a return location for a #GError, or %NULL
+ *
+ * Changes the location of a recently used resource from @uri to @new_uri.
+ * 
+ * Please note that this function will not affect the resource pointed
+ * by the URIs, but only the URI used in the recently used resources list.
+ *
+ * Return value: %TRUE on success.
+ *
+ * Since: 2.10
+ */ 
+gboolean
+gtk_recent_manager_move_item (GtkRecentManager  *recent_manager,
+                             const gchar       *uri,
+                             const gchar       *new_uri,
+                             GError           **error)
+{
+  GtkRecentManagerPrivate *priv;
+  GError *move_error;
+  gboolean res;
+  
+  g_return_val_if_fail (GTK_IS_RECENT_MANAGER (recent_manager), FALSE);
+  g_return_val_if_fail (uri != NULL, FALSE);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+  
+  priv = recent_manager->priv;
+
+  if (!g_bookmark_file_has_item (priv->recent_items, uri))
+    {
+      g_set_error (error, GTK_RECENT_MANAGER_ERROR,
+                  GTK_RECENT_MANAGER_ERROR_NOT_FOUND,
+                  _("Unable to find an item with URI '%s'"),
+                  uri);
+      return FALSE;
+    }
+  
+  move_error = NULL;
+  res = g_bookmark_file_move_item (priv->recent_items,
+                                   uri, new_uri,
+                                   &move_error);
+  if (move_error)
+    {
+      g_propagate_error (error, move_error);
+      return FALSE;
+    }
+  
+  priv->is_dirty = TRUE;
+
+  gtk_recent_manager_changed (recent_manager);
+  
+  return TRUE;
+}
+
+/**
+ * gtk_recent_manager_get_items:
+ * @recent_manager: a #GtkRecentManager
+ *
+ * Gets the list of recently used resources.
+ *
+ * Return value: a list of newly allocated #GtkRecentInfo objects. Use
+ *   gtk_recent_info_unref() on each item inside the list, and then
+ *   free the list itself using g_list_free().
+ *
+ * Since: 2.10
+ */
+GList *
+gtk_recent_manager_get_items (GtkRecentManager *recent_manager)
+{
+  GtkRecentManagerPrivate *priv;
+  GList *retval = NULL;
+  gchar **uris;
+  gsize uris_len, i;
+  
+  g_return_val_if_fail (GTK_IS_RECENT_MANAGER (recent_manager), NULL);
+  
+  priv = recent_manager->priv;
+  if (!priv->recent_items)
+    return NULL;
+  
+  uris = g_bookmark_file_get_uris (priv->recent_items, &uris_len);
+  for (i = 0; i < uris_len; i++)
+    {
+      GtkRecentInfo *info;
+      gboolean res;
+      
+      info = gtk_recent_info_new (uris[i]);
+      res = build_recent_info (priv->recent_items, info);
+      if (!res)
+        {
+          g_warning ("Unable to create a RecentInfo object for "
+                     "item with URI `%s'",
+                     uris[i]);
+          gtk_recent_info_free (info);
+         
+          continue;
+        }
+      
+      retval = g_list_prepend (retval, info);
+    }
+  
+  g_strfreev (uris);
+    
+  /* clamp the list, if a limit is present */
+  if ((priv->limit != -1) &&
+      (g_list_length (retval) > priv->limit))
+    {
+      GList *clamp, *l;
+      
+      clamp = g_list_nth (retval, priv->limit - 1);
+      
+      if (!clamp)
+        return retval;
+      
+      l = clamp->next;
+      clamp->next = NULL;
+      
+      g_list_foreach (l, (GFunc) gtk_recent_info_free, NULL);
+      g_list_free (l);
+    }
+  
+  return retval;
+}
+
+static void
+purge_recent_items_list (GtkRecentManager  *manager,
+                        GError           **error)
+{
+  GtkRecentManagerPrivate *priv = manager->priv;
+
+  if (!priv->recent_items)
+    return;
+  
+  g_bookmark_file_free (priv->recent_items);
+  priv->recent_items = NULL;
+      
+  priv->recent_items = g_bookmark_file_new ();
+  priv->size = 0;
+  priv->is_dirty = TRUE;
+      
+  /* emit the changed signal, to ensure that the purge is written */
+  gtk_recent_manager_changed (manager);
+}
+
+/**
+ * gtk_recent_manager_purge_items:
+ * @recent_manager: a #GtkRecentManager
+ * @error: a return location for a #GError, or %NULL
+ *
+ * Purges every item from the recently used resources list.
+ *
+ * Return value: the number of items that have been removed from the
+ *   recently used resources list.
+ *
+ * Since: 2.10
+ */
+gint
+gtk_recent_manager_purge_items (GtkRecentManager  *recent_manager,
+                               GError           **error)
+{
+  GtkRecentManagerPrivate *priv;
+  gint count, purged;
+  
+  g_return_val_if_fail (GTK_IS_RECENT_MANAGER (recent_manager), -1);
+
+  priv = recent_manager->priv;
+  if (!priv->recent_items)
+    return 0;
+  
+  count = g_bookmark_file_get_size (priv->recent_items);
+  if (!count)
+    return 0;
+  
+  purge_recent_items_list (recent_manager, error);
+  
+  purged = count - g_bookmark_file_get_size (priv->recent_items);
+
+  return purged;
+}
+
+static void
+gtk_recent_manager_changed (GtkRecentManager *recent_manager)
+{
+  g_signal_emit (recent_manager, signal_changed, 0);
+}
+
+/*****************
+ * GtkRecentInfo *
+ *****************/
+GType
+gtk_recent_info_get_type (void)
+{
+  static GType info_type = 0;
+  
+  if (!info_type)
+    info_type = g_boxed_type_register_static ("GtkRecentInfo",
+                                             (GBoxedCopyFunc) gtk_recent_info_ref,
+                                             (GBoxedFreeFunc) gtk_recent_info_unref);
+  return info_type;
+}
+
+static GtkRecentInfo *
+gtk_recent_info_new (const gchar *uri)
+{
+  GtkRecentInfo *info;
+
+  g_assert (uri != NULL);
+
+  info = g_new0 (GtkRecentInfo, 1);
+  info->uri = g_strdup (uri);
+  
+  info->applications = NULL;
+  info->apps_lookup = g_hash_table_new (g_str_hash, g_str_equal);
+  
+  info->groups = NULL;
+  
+  info->ref_count = 1;
+
+  return info;
+}
+
+static void
+gtk_recent_info_free (GtkRecentInfo *recent_info)
+{
+  if (!recent_info)
+    return;
+
+  g_free (recent_info->uri);
+  g_free (recent_info->display_name);
+  g_free (recent_info->description);
+  g_free (recent_info->mime_type);
+  
+  if (recent_info->applications)
+    {
+      g_slist_foreach (recent_info->applications,
+                      (GFunc) recent_app_info_free,
+                      NULL);
+      g_slist_free (recent_info->applications);
+      
+      recent_info->applications = NULL;
+    }
+  
+  if (recent_info->apps_lookup)
+    g_hash_table_destroy (recent_info->apps_lookup);
+
+  if (recent_info->groups)
+    {
+      g_slist_foreach (recent_info->groups,
+                      (GFunc) g_free,
+                      NULL);
+      g_slist_free (recent_info->groups);
+
+      recent_info->groups = NULL;
+    }
+  
+  if (recent_info->icon)
+    g_object_unref (recent_info->icon);
+
+  g_free (recent_info);
+}
+
+/**
+ * gtk_recent_info_ref:
+ * @info: a #GtkRecentInfo
+ *
+ * Increases the reference count of @recent_info by one.
+ *
+ * Return value: the recent info object with its reference count increased
+ *   by one.
+ *
+ * Since: 2.10
+ */
+GtkRecentInfo *
+gtk_recent_info_ref (GtkRecentInfo *info)
+{
+  g_return_val_if_fail (info != NULL, NULL);
+  g_return_val_if_fail (info->ref_count > 0, NULL);
+  
+  info->ref_count += 1;
+    
+  return info;
+}
+
+/**
+ * gtk_recent_info_unref:
+ * @info: a #GtkRecentInfo
+ *
+ * Decreases the reference count of @info by one.  If the reference
+ * count reaches zero, @info is deallocated, and the memory freed.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_info_unref (GtkRecentInfo *info)
+{
+  g_return_if_fail (info != NULL);
+  g_return_if_fail (info->ref_count > 0);
+
+  info->ref_count -= 1;
+  
+  if (info->ref_count == 0)
+    gtk_recent_info_free (info);
+}
+
+/**
+ * gtk_recent_info_get_uri:
+ * @info: a #GtkRecentInfo
+ *
+ * Gets the URI of the resource.
+ *
+ * Return value: the URI of the resource.  The returned string is
+ *   owned by the recent manager, and should not be freed.
+ *
+ * Since: 2.10
+ */
+G_CONST_RETURN gchar *
+gtk_recent_info_get_uri (GtkRecentInfo *info)
+{
+  g_return_val_if_fail (info != NULL, NULL);
+  
+  return info->uri;
+}
+
+/**
+ * gtk_recent_info_get_display_name:
+ * @info: a #GtkRecentInfo
+ *
+ * Gets the name of the resource.  If none has been defined, the basename
+ * of the resource is obtained.
+ *
+ * Return value: the display name of the resource.  The returned string
+ *   is owned by the recent manager, and should not be freed.
+ *
+ * Since: 2.10
+ */
+G_CONST_RETURN gchar *
+gtk_recent_info_get_display_name (GtkRecentInfo *info)
+{
+  g_return_val_if_fail (info != NULL, NULL);
+  
+  if (!info->display_name)
+    info->display_name = gtk_recent_info_get_short_name (info);
+  
+  return info->display_name;
+}
+
+/**
+ * gtk_recent_info_get_description:
+ * @info: a #GtkRecentInfo
+ *
+ * Gets the (short) description of the resource.
+ *
+ * Return value: the description of the resource.  The returned string
+ *   is owned by the recent manager, and should not be freed.
+ *
+ * Since: 2.10
+ **/
+G_CONST_RETURN gchar *
+gtk_recent_info_get_description (GtkRecentInfo *info)
+{
+  g_return_val_if_fail (info != NULL, NULL);
+  
+  return info->description;
+}
+
+/**
+ * gtk_recent_info_get_mime_type:
+ * @info: a #GtkRecentInfo
+ *
+ * Gets the MIME type of the resource.
+ *
+ * Return value: the MIME type of the resource.  The returned string
+ *   is owned by the recent manager, and should not be freed.
+ *
+ * Since: 2.10
+ */
+G_CONST_RETURN gchar *
+gtk_recent_info_get_mime_type (GtkRecentInfo *info)
+{
+  g_return_val_if_fail (info != NULL, NULL);
+  
+  if (!info->mime_type)
+    info->mime_type = g_strdup (GTK_RECENT_DEFAULT_MIME);
+  
+  return info->mime_type;
+}
+
+/**
+ * gtk_recent_info_get_added:
+ * @info: a #GtkRecentInfo
+ *
+ * Gets the timestamp (seconds from system's Epoch) when the resource
+ * was added to the recently used resources list.
+ *
+ * Return value: the number of seconds elapsed from system's Epoch when
+ *   the resource was added to the list, or -1 on failure.
+ *
+ * Since: 2.10
+ */
+time_t
+gtk_recent_info_get_added (GtkRecentInfo *info)
+{
+  g_return_val_if_fail (info != NULL, (time_t) -1);
+  
+  return info->added;
+}
+
+/**
+ * gtk_recent_info_get_modified:
+ * @info: a #GtkRecentInfo
+ *
+ * Gets the timestamp (seconds from system's Epoch) when the resource
+ * was last modified.
+ *
+ * Return value: the number of seconds elapsed from system's Epoch when
+ *   the resource was last modified, or -1 on failure.
+ *
+ * Since: 2.10
+ */
+time_t
+gtk_recent_info_get_modified (GtkRecentInfo *info)
+{
+  g_return_val_if_fail (info != NULL, (time_t) -1);
+  
+  return info->modified;
+}
+
+/**
+ * gtk_recent_info_get_visited:
+ * @info: a #GtkRecentInfo
+ *
+ * Gets the timestamp (seconds from system's Epoch) when the resource
+ * was last visited.
+ *
+ * Return value: the number of seconds elapsed from system's Epoch when
+ *   the resource was last visited, or -1 on failure.
+ *
+ * Since: 2.10
+ */
+time_t
+gtk_recent_info_get_visited (GtkRecentInfo *info)
+{
+  g_return_val_if_fail (info != NULL, (time_t) -1);
+  
+  return info->visited;
+}
+
+/**
+ * gtk_recent_info_get_private_hint:
+ * @info: a #GtkRecentInfo
+ *
+ * Gets the value of the "private" flag.  Resources in the recently used
+ * list that have this flag set to %TRUE should only be displayed by the
+ * applications that have registered them.
+ *
+ * Return value: %TRUE if the private flag was found, %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_info_get_private_hint (GtkRecentInfo *info)
+{
+  g_return_val_if_fail (info != NULL, FALSE);
+  
+  return info->is_private;
+}
+
+
+static RecentAppInfo *
+recent_app_info_new (const gchar *app_name)
+{
+  RecentAppInfo *app_info;
+
+  g_assert (app_name != NULL);
+  
+  app_info = g_new0 (RecentAppInfo, 1);
+  app_info->name = g_strdup (app_name);
+  app_info->exec = NULL;
+  app_info->count = 1;
+  app_info->stamp = time (NULL);
+  
+  return app_info;
+}
+
+static void
+recent_app_info_free (RecentAppInfo *app_info)
+{
+  if (!app_info)
+    return;
+  
+  if (app_info->name)
+    g_free (app_info->name);
+  
+  if (app_info->exec)
+    g_free (app_info->exec);
+  
+  g_free (app_info);
+}
+
+/**
+ * gtk_recent_info_get_application_info:
+ * @info: a #GtkRecentInfo
+ * @app_name: the name of the application that has registered this item
+ * @app_exec: return location for the string containing the command line
+ * @count: return location for the number of times this item was registered
+ * @time: return location for the timestamp this item was last registered
+ *    for this application
+ *
+ * Gets the data regarding the application that has registered the resource
+ * pointed by @info.
+ *
+ * If the command line contains any escape characters defined inside the
+ * storage specification, they will be expanded.
+ *
+ * Return value: %TRUE if an application with @app_name has registered this
+ *   resource inside the recently used list, or %FALSE otherwise.  You should
+ *   free the returned command line using g_free().
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_info_get_application_info (GtkRecentInfo  *info,
+                                     const gchar    *app_name,
+                                     gchar         **app_exec,
+                                     guint          *count,
+                                     time_t         *time)
+{
+  RecentAppInfo *ai;
+  
+  g_return_val_if_fail (info != NULL, FALSE);
+  g_return_val_if_fail (app_name != NULL, FALSE);
+  
+  ai = (RecentAppInfo *) g_hash_table_lookup (info->apps_lookup,
+                                             app_name);
+  if (!ai)
+    {
+      g_warning ("No registered application with name '%s' "
+                 "for item with URI '%s' found",
+                 app_name,
+                 info->uri);
+      return FALSE;
+    }
+  
+  if (app_exec)
+    *app_exec = ai->exec;
+  
+  if (count)
+    *count = ai->count;
+  
+  if (time)
+    *time = ai->stamp;
+
+  return TRUE;
+}
+
+/**
+ * gtk_recent_info_get_applications:
+ * @info: a #GtkRecentInfo
+ * @length: return location for the length of the returned list, or %NULL
+ *
+ * Retrieves the list of applications that have registered this resource.
+ *
+ * Return value: a newly allocated %NULL-terminated array of strings.
+ *   Use g_strfreev() to free it.
+ *
+ * Since: 2.10
+ */                          
+gchar **
+gtk_recent_info_get_applications (GtkRecentInfo *info,
+                                 gsize         *length)
+{
+  GSList *l;
+  gchar **retval;
+  gsize n_apps, i;
+  
+  g_return_val_if_fail (info != NULL, NULL);
+  
+  if (!info->applications)
+    {
+      if (length)
+        *length = 0;
+      
+      return NULL;    
+    }
+  
+  n_apps = g_slist_length (info->applications);
+  
+  retval = g_new0 (gchar *, n_apps + 1);
+  
+  for (l = info->applications, i = 0;
+       l != NULL;
+       l = l->next)
+    {
+      RecentAppInfo *ai = (RecentAppInfo *) l->data;
+      
+      g_assert (ai != NULL);
+      
+      retval[i++] = g_strdup (ai->name);
+    }
+  retval[i] = NULL;
+  
+  if (length)
+    *length = i;
+  
+  return retval;
+}
+
+/**
+ * gtk_recent_info_has_application:
+ * @info: a #GtkRecentInfo
+ * @app_name: a string containing an application name
+ *
+ * Checks whether an application registered this resource using @app_name.
+ *
+ * Return value: %TRUE if an application with name @app_name was found,
+ *   %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_info_has_application (GtkRecentInfo *info,
+                                const gchar   *app_name)
+{
+  g_return_val_if_fail (info != NULL, FALSE);
+  g_return_val_if_fail (app_name != NULL, FALSE);
+  
+  return (NULL != g_hash_table_lookup (info->apps_lookup, app_name));
+}
+
+/**
+ * gtk_recent_info_last_application:
+ * @info: a #GtkRecentInfo
+ *
+ * Gets the name of the last application that have registered the
+ * recently used resource represented by @info.
+ *
+ * Return value: an application name.  Use g_free() to free it.
+ *
+ * Since: 2.10
+ */
+gchar *
+gtk_recent_info_last_application (GtkRecentInfo  *info)
+{
+  GSList *l;
+  time_t last_stamp = (time_t) -1;
+  gchar *name = NULL;
+  
+  g_return_val_if_fail (info != NULL, NULL);
+  
+  for (l = info->applications; l != NULL; l = l->next)
+    {
+      RecentAppInfo *ai = (RecentAppInfo *) l->data;
+      
+      if (ai->stamp > last_stamp)
+        name = ai->name;
+    }
+  
+  return g_strdup (name);
+}
+
+typedef struct
+{
+  gint size;
+  GdkPixbuf *pixbuf;
+} IconCacheElement;
+
+static void
+icon_cache_element_free (IconCacheElement *element)
+{
+  if (element->pixbuf)
+    g_object_unref (element->pixbuf);
+  g_free (element);
+}
+
+static void
+icon_theme_changed (GtkIconTheme     *icon_theme)
+{
+  GHashTable *cache;
+
+  /* Difference from the initial creation is that we don't
+   * reconnect the signal
+   */
+  cache = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                (GDestroyNotify)g_free,
+                                (GDestroyNotify)icon_cache_element_free);
+  g_object_set_data_full (G_OBJECT (icon_theme), "gtk-recent-icon-cache",
+                         cache, (GDestroyNotify)g_hash_table_destroy);
+}
+
+/* TODO: use the GtkFileChooser's icon cache instead of our own to reduce
+ * the memory footprint
+ */
+static GdkPixbuf *
+get_cached_icon (const gchar *name,
+                gint         pixel_size)
+{
+  GtkIconTheme *icon_theme;
+  GHashTable *cache;
+  IconCacheElement *element;
+
+  icon_theme = gtk_icon_theme_get_default ();
+  cache = g_object_get_data (G_OBJECT (icon_theme), "gtk-recent-icon-cache");
+
+  if (!cache)
+    {
+      cache = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                    (GDestroyNotify)g_free,
+                                    (GDestroyNotify)icon_cache_element_free);
+
+      g_object_set_data_full (G_OBJECT (icon_theme), "gtk-recent-icon-cache",
+                             cache, (GDestroyNotify)g_hash_table_destroy);
+      g_signal_connect (icon_theme, "changed",
+                       G_CALLBACK (icon_theme_changed), NULL);
+    }
+
+  element = g_hash_table_lookup (cache, name);
+  if (!element)
+    {
+      element = g_new0 (IconCacheElement, 1);
+      g_hash_table_insert (cache, g_strdup (name), element);
+    }
+
+  if (element->size != pixel_size)
+    {
+      if (element->pixbuf)
+       g_object_unref (element->pixbuf);
+
+      element->size = pixel_size;
+      element->pixbuf = gtk_icon_theme_load_icon (icon_theme, name,
+                                                 pixel_size, 0, NULL);
+    }
+
+  return element->pixbuf ? g_object_ref (element->pixbuf) : NULL;
+}
+
+
+static GdkPixbuf *
+get_icon_for_mime_type (const char *mime_type,
+                       gint        pixel_size)
+{
+  const char *separator;
+  GString *icon_name;
+  GdkPixbuf *pixbuf;
+
+  separator = strchr (mime_type, '/');
+  if (!separator)
+    return NULL; /* maybe we should return a GError with "invalid MIME-type" */
+
+  icon_name = g_string_new ("gnome-mime-");
+  g_string_append_len (icon_name, mime_type, separator - mime_type);
+  g_string_append_c (icon_name, '-');
+  g_string_append (icon_name, separator + 1);
+  pixbuf = get_cached_icon (icon_name->str, pixel_size);
+  g_string_free (icon_name, TRUE);
+  if (pixbuf)
+    return pixbuf;
+
+  icon_name = g_string_new ("gnome-mime-");
+  g_string_append_len (icon_name, mime_type, separator - mime_type);
+  pixbuf = get_cached_icon (icon_name->str, pixel_size);
+  g_string_free (icon_name, TRUE);
+
+  return pixbuf;
+}
+
+static GdkPixbuf *
+get_icon_fallback (const gchar *icon_name,
+                  gint         size)
+{
+  GtkIconTheme *icon_theme;
+  GdkPixbuf *retval;
+
+  icon_theme = gtk_icon_theme_get_default ();
+  
+  retval = gtk_icon_theme_load_icon (icon_theme, icon_name,
+                                    size,
+                                    GTK_ICON_LOOKUP_USE_BUILTIN,
+                                    NULL);
+  g_assert (retval != NULL);
+  
+  return retval; 
+}
+
+/**
+ * gtk_recent_info_get_icon:
+ * @info: a #GtkRecentInfo
+ * @size: the size of the icon in pixels
+ *
+ * Retrieves the icon of size @size associated to the resource MIME type.
+ *
+ * Return value: a #GdkPixbuf containing the icon, or %NULL.
+ *
+ * Since: 2.10
+ */
+GdkPixbuf *
+gtk_recent_info_get_icon (GtkRecentInfo *info,
+                         gint           size)
+{
+  GdkPixbuf *retval = NULL;
+  
+  g_return_val_if_fail (info != NULL, NULL);
+  
+  if (info->mime_type)
+    retval = get_icon_for_mime_type (info->mime_type, size);
+
+  /* this should never fail */  
+  if (!retval)
+    retval = get_icon_fallback (GTK_STOCK_FILE, size);
+  
+  return retval;
+}
+
+/**
+ * gtk_recent_info_is_local:
+ * @info: a #GtkRecentInfo
+ *
+ * Checks whether the resource is local or not by looking at the
+ * scheme of its URI.
+ *
+ * Return value: %TRUE if the resource is local.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_info_is_local (GtkRecentInfo *info)
+{
+  g_return_val_if_fail (info != NULL, FALSE);
+  
+  return g_str_has_prefix (info->uri, "file://");
+}
+
+/**
+ * gtk_recent_info_exists:
+ * @info: a #GtkRecentInfo
+ *
+ * Checks whether the resource pointed by @info still exists.  At
+ * the moment this check is done only on resources pointing to local files.
+ *
+ * Return value: %TRUE if the resource exists
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_info_exists (GtkRecentInfo *info)
+{
+  gchar *filename;
+  struct stat stat_buf;
+  gboolean retval = FALSE;
+  
+  g_return_val_if_fail (info != NULL, FALSE);
+  
+  /* we guarantee only local resources */
+  if (!gtk_recent_info_is_local (info))
+    return FALSE;
+  
+  filename = g_filename_from_uri (info->uri, NULL, NULL);
+  if (filename)
+    {
+      if (stat (filename, &stat_buf) == 0)
+        retval = TRUE;
+     
+      g_free (filename);
+    }
+  
+  return retval;
+}
+
+/**
+ * gtk_recent_info_match:
+ * @a: a #GtkRecentInfo
+ * @b: a #GtkRecentInfo
+ *
+ * Checks whether two #GtkRecentInfo structures point to the same
+ * resource.
+ *
+ * Return value: %TRUE if both #GtkRecentInfo structures point to se same
+ *   resource, %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_info_match (GtkRecentInfo *a,
+                      GtkRecentInfo *b)
+{
+  g_return_val_if_fail (a != NULL, FALSE);
+  g_return_val_if_fail (b != NULL, FALSE);
+  
+  return (0 == strcmp (a->uri, b->uri));
+}
+
+/* taken from gnome-vfs-uri.c */
+static const gchar *
+get_method_string (const gchar *substring, gchar **method_string)
+{
+  const gchar *p;
+  char *method;
+       
+  for (p = substring;
+       g_ascii_isalnum (*p) || *p == '+' || *p == '-' || *p == '.';
+       p++)
+    ;
+
+  if (*p == ':'
+#ifdef G_OS_WIN32
+                &&
+      !(p == substring + 1 && g_ascii_isalpha (*substring))
+#endif
+                                                          )
+    {
+      /* Found toplevel method specification.  */
+      method = g_strndup (substring, p - substring);
+      *method_string = g_ascii_strdown (method, -1);
+      g_free (method);
+      p++;
+    }
+  else
+    {
+      *method_string = g_strdup ("file");
+      p = substring;
+    }
+  
+  return p;
+}
+
+/* Stolen from gnome_vfs_make_valid_utf8() */
+static char *
+make_valid_utf8 (const char *name)
+{
+  GString *string;
+  const char *remainder, *invalid;
+  int remaining_bytes, valid_bytes;
+
+  string = NULL;
+  remainder = name;
+  remaining_bytes = name ? strlen (name) : 0;
+
+  while (remaining_bytes != 0)
+    {
+      if (g_utf8_validate (remainder, remaining_bytes, &invalid))
+        break;
+      
+      valid_bytes = invalid - remainder;
+      
+      if (string == NULL)
+        string = g_string_sized_new (remaining_bytes);
+      
+      g_string_append_len (string, remainder, valid_bytes);
+      g_string_append_c (string, '?');
+      
+      remaining_bytes -= valid_bytes + 1;
+      remainder = invalid + 1;
+    }
+  
+  if (string == NULL)
+    return g_strdup (name);
+
+  g_string_append (string, remainder);
+  g_assert (g_utf8_validate (string->str, -1, NULL));
+
+  return g_string_free (string, FALSE);
+}
+
+static gchar *
+get_uri_shortname_for_display (const gchar *uri)
+{
+  gchar *name = NULL;
+  gboolean validated = FALSE;
+
+  if (g_str_has_prefix (uri, "file://"))
+    {
+      gchar *local_file;
+      
+      local_file = g_filename_from_uri (uri, NULL, NULL);
+      
+      if (local_file != NULL)
+        {
+          name = g_filename_display_basename (local_file);
+          validated = TRUE;
+        }
+               
+      g_free (local_file);
+    } 
+  else
+    {
+      gchar *method;
+      gchar *local_file;
+      const gchar *rest;
+      
+      rest = get_method_string (uri, &method);
+      local_file = g_filename_display_basename (rest);
+      
+      name = g_strdup_printf ("%s: %s", method, local_file);
+      
+      g_free (local_file);
+      g_free (method);
+    }
+  
+  g_assert (name != NULL);
+  
+  if (!validated && !g_utf8_validate (name, -1, NULL))
+    {
+      gchar *utf8_name;
+      
+      utf8_name = make_valid_utf8 (name);
+      g_free (name);
+      
+      name = utf8_name;
+    }
+
+  return name;
+}
+
+/**
+ * gtk_recent_info_get_short_name:
+ * @info: an #GtkRecentInfo
+ *
+ * Computes a valid UTF-8 string that can be used as the name of the item in a
+ * menu or list.  For example, calling this function on an item that refers to
+ * "file:///foo/bar.txt" will yield "bar.txt".
+ *
+ * Return value: A newly-allocated string in UTF-8 encoding; free it with
+ *   g_free().
+ *
+ * Since: 2.10
+ */
+gchar *
+gtk_recent_info_get_short_name (GtkRecentInfo *info)
+{
+  gchar *short_name;
+
+  g_return_val_if_fail (info != NULL, NULL);
+
+  if (info->uri == NULL)
+    return NULL;
+
+  short_name = get_uri_shortname_for_display (info->uri);
+
+  return short_name;
+}
+
+/**
+ * gtk_recent_info_get_uri_display:
+ * @info: a #GtkRecentInfo
+ *
+ * Gets a displayable version of the resource's URI.
+ *
+ * Return value: a UTF-8 string containing the resource's URI or %NULL
+ *
+ * Since: 2.10
+ */
+gchar *
+gtk_recent_info_get_uri_display (GtkRecentInfo *info)
+{
+  gchar *filename, *filename_utf8;
+  
+  g_return_val_if_fail (info != NULL, NULL);
+  
+  filename = g_filename_from_uri (info->uri, NULL, NULL);
+  if (!filename)
+    return NULL;
+      
+  filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
+  g_free (filename);
+
+  return filename_utf8;
+}
+
+/**
+ * gtk_recent_info_get_age:
+ * @info: a #GtkRecentInfo
+ *
+ * Gets the number of days elapsed since the last update of the resource
+ * pointed by @info.
+ *
+ * Return value: a positive integer containing the number of days elapsed
+ *   since the time this resource was last modified.  On failure, -1 is
+ *   returned.
+ *
+ * Since: 2.10
+ */
+gint
+gtk_recent_info_get_age (GtkRecentInfo *info)
+{
+  time_t now, delta;
+  gint retval;
+
+  g_return_val_if_fail (info != NULL, -1);
+
+  now = time (NULL);
+  
+  delta = now - info->modified;
+  g_assert (delta >= 0);
+  
+  retval = (gint) (delta / (60 * 60 * 24));
+  
+  return retval;
+}
+
+/**
+ * gtk_recent_info_get_groups:
+ * @info: a #GtkRecentInfo
+ * @length: return location for the number of groups returned, or %NULL
+ *
+ * Returns all groups registered for the recently used item @info.  The
+ * array of returned group names will be %NULL terminated, so length might
+ * optionally be %NULL.
+ *
+ * Return value: a newly allocated %NULL terminated array of strings.  Use
+ *   g_strfreev() to free it.
+ *
+ * Since: 2.10
+ */
+gchar **
+gtk_recent_info_get_groups (GtkRecentInfo *info,
+                           gsize         *length)
+{
+  GSList *l;
+  gchar **retval;
+  gsize n_groups, i;
+  
+  g_return_val_if_fail (info != NULL, NULL);
+  
+  if (!info->groups)
+    {
+      if (length)
+        *length = 0;
+      
+      return NULL;
+    }
+  
+  n_groups = g_slist_length (info->groups);
+  
+  retval = g_new0 (gchar *, n_groups + 1);
+  
+  for (l = info->groups, i = 0;
+       l != NULL;
+       l = l->next)
+    {
+      gchar *group_name = (gchar *) l->data;
+      
+      g_assert (group_name != NULL);
+      
+      retval[i++] = g_strdup (group_name);
+    }
+  retval[i] = NULL;
+  
+  if (length)
+    *length = i;
+  
+  return retval;
+}
+
+/**
+ * gtk_recent_info_has_group:
+ * @info: a #GtkRecentInfo
+ * @group_name: name of a group
+ *
+ * Checks whether @group_name appears inside the groups registered for the
+ * recently used item @info.
+ *
+ * Return value: %TRUE if the group was found.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_info_has_group (GtkRecentInfo *info,
+                          const gchar   *group_name)
+{
+  GSList *l;
+  
+  g_return_val_if_fail (info != NULL, FALSE);
+  g_return_val_if_fail (group_name != NULL, FALSE);
+
+  if (!info->groups)
+    return FALSE;
+
+  for (l = info->groups; l != NULL; l = l->next)
+    {
+      gchar *g = (gchar *) l->data;
+
+      if (strcmp (g, group_name) == 0)
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+#define __GTK_RECENT_MANAGER_C__
+#include "gtkaliasdef.c"
diff --git a/gtk/gtkrecentmanager.h b/gtk/gtkrecentmanager.h
new file mode 100644 (file)
index 0000000..f41e2da
--- /dev/null
@@ -0,0 +1,214 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentmanager.h: a manager for the recently used resources
+ *
+ * Copyright (C) 2006 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ */
+
+#ifndef __GTK_RECENT_MANAGER_H__
+#define __GTK_RECENT_MANAGER_H__
+
+#include <glib-object.h>
+#include <gdk/gdkscreen.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <time.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_RECENT_INFO                   (gtk_recent_info_get_type ())
+
+#define GTK_TYPE_RECENT_MANAGER                        (gtk_recent_manager_get_type ())
+#define GTK_RECENT_MANAGER(obj)                        (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_RECENT_MANAGER, GtkRecentManager))
+#define GTK_IS_RECENT_MANAGER(obj)             (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_RECENT_MANAGER))
+#define GTK_RECENT_MANAGER_CLASS(klass)        (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_RECENT_MANAGER, GtkRecentManagerClass))
+#define GTK_IS_RECENT_MANAGER_CLASS(klass)     (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_RECENT_MANAGER))
+#define GTK_RECENT_MANAGER_GET_CLASS(obj)      (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_RECENT_MANAGER, GtkRecentManagerClass))
+
+typedef struct _GtkRecentInfo          GtkRecentInfo;
+typedef struct _GtkRecentData          GtkRecentData;
+typedef struct _GtkRecentManager       GtkRecentManager;
+typedef struct _GtkRecentManagerClass  GtkRecentManagerClass;
+typedef struct _GtkRecentManagerPrivate GtkRecentManagerPrivate;
+
+/**
+ * GtkRecentData:
+ *
+ * @display_name: a UTF-8 encoded string, containing the name of the recently
+ *   used resource to be displayed, or %NULL;
+ * @description: a UTF-8 encoded string, containing a short description of
+ *   the resource, or %NULL;
+ * @mime_type: the MIME type of the resource;
+ * @app_name: the name of the application that is registering this recently
+ *   used resource;
+ * @app_exec: command line used to launch this resource; may contain the
+ *   "%f" and "%u" escape characters which will be expanded to the resource
+ *   file path and URI respectively when the command line is retrieved;
+ * @groups: a vector of strings containing groups names;
+ * @is_private: whether this resource should be displayed only by the
+ *   applications that have registered it or not.
+ *
+ * Meta-data to be passed to gtk_recent_manager_add_full() when
+ * registering a recently used resource.
+ **/
+struct _GtkRecentData
+{
+  gchar *display_name;
+  gchar *description;
+  
+  gchar *mime_type;
+  
+  gchar *app_name;
+  gchar *app_exec;
+  
+  gchar **groups;
+  
+  gboolean is_private;
+};
+
+struct _GtkRecentManager
+{
+  /*< private >*/
+  GObject parent_instance;
+  
+  GtkRecentManagerPrivate *priv;
+};
+
+struct _GtkRecentManagerClass
+{
+  /*< private >*/
+  GObjectClass parent_class;
+  
+  void (*changed) (GtkRecentManager *manager);
+  
+  /* padding for future expansion */
+  void (*_gtk_recent1) (void);
+  void (*_gtk_recent2) (void);
+  void (*_gtk_recent3) (void);
+  void (*_gtk_recent4) (void);
+};
+
+/**
+ * GtkRecentManagerError:
+ * @GTK_RECENT_MANAGER_ERROR_NOT_FOUND: the URI specified does not exists in
+ *   the recently used resources list.
+ * @GTK_RECENT_MANAGER_ERROR_INVALID_URI: the URI specified is not valid.
+ * @GTK_RECENT_MANAGER_ERROR_INVALID_MIME: the MIME type specified is not
+ *   valid.
+ * @GTK_RECENT_MANAGER_ERROR_INVALID_ENCODING: the supplied string is not
+ *   UTF-8 encoded.
+ * @GTK_RECENT_MANAGER_ERROR_NOT_REGISTERED: no application has registered
+ *   the specified item.
+ * @GTK_RECENT_MANAGER_ERROR_READ: failure while reading the recently used
+ *   resources file.
+ * @GTK_RECENT_MANAGER_ERROR_WRITE: failure while writing the recently used
+ *   resources file.
+ * @GTK_RECENT_MANAGER_ERROR_UNKNOWN: unspecified error.
+ *
+ * Error codes for GtkRecentManager operations
+ **/
+typedef enum
+{
+  GTK_RECENT_MANAGER_ERROR_NOT_FOUND,
+  GTK_RECENT_MANAGER_ERROR_INVALID_URI,
+  GTK_RECENT_MANAGER_ERROR_INVALID_MIME,
+  GTK_RECENT_MANAGER_ERROR_INVALID_ENCODING,
+  GTK_RECENT_MANAGER_ERROR_NOT_REGISTERED,
+  GTK_RECENT_MANAGER_ERROR_BAD_EXEC_STRING,
+  GTK_RECENT_MANAGER_ERROR_READ,
+  GTK_RECENT_MANAGER_ERROR_WRITE,
+  GTK_RECENT_MANAGER_ERROR_UNKNOWN
+} GtkRecentManagerError;
+
+#define GTK_RECENT_MANAGER_ERROR       (gtk_recent_manager_error_quark ())
+GQuark         gtk_recent_manager_error_quark (void);
+
+
+GType            gtk_recent_manager_get_type       (void) G_GNUC_CONST;
+
+GtkRecentManager *gtk_recent_manager_new            (void);
+GtkRecentManager *gtk_recent_manager_get_default    (void);
+GtkRecentManager *gtk_recent_manager_get_for_screen (GdkScreen            *screen);
+
+void              gtk_recent_manager_set_screen     (GtkRecentManager     *manager,
+                                                    GdkScreen            *screen);
+
+gboolean          gtk_recent_manager_add_item       (GtkRecentManager     *manager,
+                                                    const gchar          *uri,
+                                                    GError              **error);
+gboolean          gtk_recent_manager_add_full       (GtkRecentManager     *manager,
+                                                    const gchar          *uri,
+                                                    const GtkRecentData  *recent_data,
+                                                    GError              **error);
+gboolean          gtk_recent_manager_remove_item    (GtkRecentManager     *manager,
+                                                    const gchar          *uri,
+                                                    GError              **error);
+GtkRecentInfo *   gtk_recent_manager_lookup_item    (GtkRecentManager     *manager,
+                                                    const gchar          *uri,
+                                                    GError              **error);
+gboolean          gtk_recent_manager_has_item       (GtkRecentManager     *manager,
+                                                    const gchar          *uri);
+gboolean          gtk_recent_manager_move_item      (GtkRecentManager     *manager,
+                                                    const gchar          *uri,
+                                                    const gchar          *new_uri,
+                                                    GError              **error);
+void              gtk_recent_manager_set_limit      (GtkRecentManager     *manager,
+                                                    gint                  limit);
+gint              gtk_recent_manager_get_limit      (GtkRecentManager     *manager);
+GList *           gtk_recent_manager_get_items      (GtkRecentManager     *manager);
+gint              gtk_recent_manager_purge_items    (GtkRecentManager     *manager,
+                                                    GError              **error);
+
+
+GType                gtk_recent_info_get_type             (void) G_GNUC_CONST;
+
+GtkRecentInfo *       gtk_recent_info_ref                  (GtkRecentInfo  *info);
+void                  gtk_recent_info_unref                (GtkRecentInfo  *info);
+
+G_CONST_RETURN gchar *gtk_recent_info_get_uri              (GtkRecentInfo  *info);
+G_CONST_RETURN gchar *gtk_recent_info_get_display_name     (GtkRecentInfo  *info);
+G_CONST_RETURN gchar *gtk_recent_info_get_description      (GtkRecentInfo  *info);
+G_CONST_RETURN gchar *gtk_recent_info_get_mime_type        (GtkRecentInfo  *info);
+time_t                gtk_recent_info_get_added            (GtkRecentInfo  *info);
+time_t                gtk_recent_info_get_modified         (GtkRecentInfo  *info);
+time_t                gtk_recent_info_get_visited          (GtkRecentInfo  *info);
+gboolean              gtk_recent_info_get_private_hint     (GtkRecentInfo  *info);
+gboolean              gtk_recent_info_get_application_info (GtkRecentInfo  *info,
+                                                           const gchar    *app_name,
+                                                           gchar         **app_exec,
+                                                           guint          *count,
+                                                           time_t         *time);
+gchar **              gtk_recent_info_get_applications     (GtkRecentInfo  *info,
+                                                           gsize          *length) G_GNUC_MALLOC;
+gchar *               gtk_recent_info_last_application     (GtkRecentInfo  *info) G_GNUC_MALLOC;
+gboolean              gtk_recent_info_has_application      (GtkRecentInfo  *info,
+                                                           const gchar    *app_name);
+gchar **              gtk_recent_info_get_groups           (GtkRecentInfo  *info,
+                                                           gsize          *length) G_GNUC_MALLOC;
+gboolean              gtk_recent_info_has_group            (GtkRecentInfo  *info,
+                                                           const gchar    *group_name);
+GdkPixbuf *           gtk_recent_info_get_icon             (GtkRecentInfo  *info,
+                                                           gint            size);
+gchar *               gtk_recent_info_get_short_name       (GtkRecentInfo  *info) G_GNUC_MALLOC;
+gchar *               gtk_recent_info_get_uri_display      (GtkRecentInfo  *info) G_GNUC_MALLOC;
+gint                  gtk_recent_info_get_age              (GtkRecentInfo  *info);
+gboolean              gtk_recent_info_is_local             (GtkRecentInfo  *info);
+gboolean              gtk_recent_info_exists               (GtkRecentInfo  *info);
+gboolean              gtk_recent_info_match                (GtkRecentInfo  *info_a,
+                                                           GtkRecentInfo  *info_b);
+
+G_END_DECLS
+
+#endif /* __GTK_RECENT_MANAGER_H__ */